
PoeTweaks
Server-synced XP, skill recovery, death audio, personal cartography atlas and inventory audit for the poe playthrough.PoeTweaks
Custom tweaks for the poe Valheim playthrough: separate combat/noncombat XP rates, combat boosts when trained skill progress is below the enemy's native biome tier, plus peak-based skill retention and recovery. Combat and noncombat rates start at 2×. Dummy XP uses a further 0.2×; each enemy star adds 1× to the star multiplier (zero/one/two stars use 1×/2×/3×). There are no flat biome multipliers.
Install the same mod version on the server and every client. Requires BepInExPack_Valheim 5.4.2351, Jotunn 2.30.2 and ZenMap 1.11.0; mod managers install these dependencies automatically. Jotunn enforces PoeTweaks' mod/version requirements and synchronizes all gameplay settings. Clients do not need to edit or export those settings in r2modman. This mod replaces FortifySkillsReborn and SkillCatchUp for this playthrough; remove those mods from the profile to avoid applying both implementations.
The generated server configuration is BepInEx/config/valde.poe.tweaks.cfg. In this repository, maintain server settings through overrides/bepinex/valde.poe.tweaks.cfg; the importer copies overrides onto the generated mod pack. Run the next manual ./setup.py import to apply those overrides and restart the server. Building this package does not install or publish it.
Experience settings
[Experience] contains five server-synced settings:
| Setting | Default | Meaning |
|---|---|---|
Enabled |
true | Enable category rates and biome/dummy/star adjustments; recovery remains separate |
CombatXpMultiplier |
2 | Base XP rate for combat weapons and blocking |
NonCombatXpMultiplier |
2 | Base XP rate for every other skill |
DummyMultiplier |
0.2 | Further combat XP rate for dummies, without biome/star bonuses |
BonusPerStar |
1 | Add 1× to the star multiplier per enemy star |
The repository sets native global skill gain to 100%. Category rates apply
once to each native XP award, including new skills with no remembered peak
and skill XP awarded by summons to their owner. Combat/noncombat use the same
classification as retention below. Noncombat receives no target/biome bonus.
Defaults give ordinary noncombat XP 2× and dummy combat XP 2 × 0.2 = 0.4×
before recovery and vanilla buffs. Disabling Enabled returns category and
target adjustments to 1×, while retaining the separate recovery feature.
Land biomes use eight ascending tiers. Their expected skill levels are evenly spaced from 12.5 to 100:
| Native biome | Tier | Expected skill |
|---|---|---|
| Meadows | 1 | 12.5 |
| BlackForest | 2 | 25 |
| Swamp | 3 | 37.5 |
| Mountain | 4 | 50 |
| Plains | 5 | 62.5 |
| Mistlands | 6 | 75 |
| AshLands | 7 | 87.5 |
| DeepNorth | 8 | 100 |
For a classified land enemy, combat XP uses:
expectedBiome = 8 × trainedSkillProgress / 100
biomeBonus = max(1, 1 + nativeBiomeTier - expectedBiome)
targetMultiplier = biomeBonus × (1 + stars × BonusPerStar)
Trained skill progress includes the XP bar and excludes equipment/status skill
bonuses. For Plains enemies, skill 0 gets 6×, skill 50 gets 2×, and skill 62.5+
gets 1× before other modifiers. Being half a biome behind adds 0.5×.
Classification uses the enemy prefab, so a swamp enemy retains its swamp tier
outside the swamp. Unknown or modded enemies, players and tamed targets receive
no biome/star bonus. Ocean enemies receive no biome bonus but retain the
independent star setting. Training dummies and archery targets use
DummyMultiplier without a biome or star bonus. The former [Experience biomes]
flat-rate settings have been removed.
Combat weapons (melee, ranged, and magic), summoned combat skill XP, and blocking use these biome/dummy rates in addition to their category multiplier. Noncombat skills receive their category multiplier and the recovery bonus described below. Multi-target attacks use the highest combined biome/star/dummy target multiplier, calculated using the actual awarded skill's trained progress, while retaining the game's XP event count. Area-spell XP is deferred until the hit batch completes; a failed batch does not award its deferred XP. Trigger-based area hits retain their immediate XP event.
Skill retention and recovery
[Skill recovery] has five server-synced settings:
| Setting | Default | Meaning |
|---|---|---|
CombatRetentionPercent |
35 | Percentage of each combat skill's personal peak restored on death |
NonCombatRetentionPercent |
35 | Percentage restored for every other skill |
StartMultiplier |
10 | Recovery XP multiplier at or below that skill's retained floor |
KneeMultiplier |
3 | Recovery XP multiplier at the boundary below |
KneePercent |
90 | Boundary as a percentage of the personal peak |
Combat means Swords, Knives, Clubs, Polearms, Spears, Blocking, Axes, Bows, ElementalMagic, BloodMagic, Unarmed and Crossbows. Every other skill, including WoodCutting and modded skills outside that list, uses noncombat retention. The recovery curve applies to all skills, with the same three curve settings and the appropriate category's retained floor.
For the default settings, recovery scales linearly from 10× at 35% of peak to 3× at 90% of peak, then linearly to 1× at the peak. At or above the peak, XP uses 1× recovery. Below the floor, recovery stays at 10×. For a peak of 60, the three anchors are level 21 at 10×, level 54 at 3× and level 60 at 1×. If retention reaches/exceeds the knee, or the knee is set to 100%, the middle anchor collapses: recovery tapers directly from StartMultiplier at the floor to 1× at the peak. Retention of 100% restores the peak on death.
Each skill's peak is its highest trained level including partial XP progress,
without equipment or status-effect bonuses. New records start from the
character's current skills; historical levels lost before installing this mod
cannot be reconstructed. Peaks never decrease, including after repeated deaths.
The mod stores them in the character's native custom data under
valde.poe.tweaks.skill-peaks.v1, so they survive save/load and server restarts.
Back up character saves as well as the server world. Missing modded skills keep
their saved peak and are restored only when their skill definition is available.
Every local player death resets known skills to the configured percentage of their peak and clears their XP accumulators. This includes deaths during the native no-skill-drain period. Native item deletion and world flags stay intact, including Hardcore's item rules. There is no separately earned fortified level.
Recovery multiplies the category and biome/dummy/star XP rates. Native global XP and other existing XP modifiers still apply. Blood-shield break XP has no enemy target, so receives recovery without a biome bonus. Native XP still grants at most one level per award and discards overflow; large stacked multipliers can waste XP on individual awards.
Death audio
The bundled seven-second Ahhhhhh Sound Effect by FreeSoundSamples
plays once when the local player dies. No runtime download is required.
Two settings under [Death audio] synchronize from the server:
| Setting | Default | Meaning |
|---|---|---|
Audience |
NearbyPlayers |
DyingPlayerOnly plays only for the dying player; NearbyPlayers also plays for clients within the radius |
Radius |
100 |
Nearby listener radius in metres; the dying player always hears the clip |
Playback uses each client's SFX/master volume and has no distance attenuation. Dedicated servers do not load or play the clip.
Personal cartography atlas
In a nomap world, reading a cartography table copies its currently charted
region into your personal atlas and opens the atlas. Press the map action
(M by default, or its controller/rebound equivalent) to open or close it.
The atlas uses the native map's pan and zoom controls. It shows terrain only:
no pins, player arrow, ship marker or player-centred view.
Reading a mature table centres the view on that copied region; reopening with
the map action remembers the last viewed position during that world session.
Copied regions are snapshots of ZenMap's table position and current reveal radius. Overlapping copies form one visible area. Revisit a table to copy its expanded region; ageing elsewhere and ordinary travel reveal nothing in the atlas. Each character must read each table independently. Copies are stored in the character's custom data, separately for each world, and survive death and save/load. Back up character saves along with the server world.
The server-synced [Atlas] setting MinimumTableAgeDays defaults to 1.
A newly built table must age for one full in-game day before it can reveal
or copy a region. During that warmup, table interactions do not centre the
atlas on the table. Set the value to 0 to disable the warmup. Afterward,
ZenMap's existing age/radius curve applies unchanged.
This integration targets ZenMap 1.11.0's internal map hooks. Keep that version pinned until compatibility with another release is verified.
Inventory audit
PoeTweaks compares the inventory loaded at join with the server's last record for that world, platform account and character. Differences are written to the server log, including every item gained or lost and its quality level. Stack splits and slot moves are ignored. Identity uses the item prefab; durability, equipment state and custom item metadata are not compared.
Example server warning:
Inventory changed while away: character "Player" (platform "7656...", character ID 123); previous snapshot clean logout at ...; +40 "Iron" (quality 1), -1 "Bow" (quality 2), +1 "Bow" (quality 3)
The first observed join establishes a baseline. Later joins compare before normal gameplay, without treating deaths or respawns as new sessions. No inventory changes, kicks or join refusals are performed by this feature.
On normal logout or application quit, the mod sends the inventory from the
successful character save before native teardown. It briefly pumps the Steam
connection until the server acknowledges durable storage, or the timeout
expires. A crash, forced kill, network loss, server kick, failed character save
or failed acknowledgement can leave only a periodic checkpoint. Differences
against those checkpoints are explicitly marked potentially stale.
Character rollbacks can also create differences. Unchanged inventory does not
prove the character never visited another world; reports are client supplied.
All three [Inventory audit] settings synchronize from the server:
| Setting | Default | Meaning |
|---|---|---|
Enabled |
true | Enable inventory audit reports |
SnapshotIntervalSeconds |
5 | Periodic checkpoint interval, minimum 5 seconds |
LogoutTimeoutSeconds |
2 | Maximum wait for final server acknowledgement, 0–10 seconds |
Server records live under <savedir>/PoeTweaks/inventory-audit/, outside
BepInEx and the imported pack. With this repository's Compose setup, that is
config/PoeTweaks/inventory-audit/ on the host. Back up this directory alongside
world saves; pack imports and docker compose down preserve it. Each record is
an atomically replaced JSON snapshot; filenames hash world/account/character
identity. Unreadable records are logged and preserved rather than silently
overwritten. This final-snapshot path targets the playthrough's Steam server;
crossplay transports have not been validated.
Build
Requires the existing server/game assemblies under data/ (including the game's Newtonsoft.Json) and Jotunn under pack/. Testing and isolated runtime checks also use the installed ZenMap 1.11.0 and Zen.ModLib. Game and dependency libraries are not included in the package.
From the repository root:
nix develop path:.
dotnet restore mods/PoeTweaks/PoeTweaks.csproj --locked-mode
dotnet build mods/PoeTweaks/PoeTweaks.csproj -c Release --no-restore
dotnet build mods/PoeTweaks/PoeTweaks.csproj -c Release --no-restore -t:Package
The package is written to mods/PoeTweaks/dist/PoeTweaks-0.3.0.zip. It contains the plugin DLL, bundled death.wav, manifest, README, and icon; it does not contain client configuration or game libraries.
Tests
From the repository root:
dotnet restore mods/PoeTweaks/tests/PoeTweaks.Tests.csproj --locked-mode
dotnet build mods/PoeTweaks/tests/PoeTweaks.Tests.csproj -c Release --no-restore
mono mods/PoeTweaks/tests/bin/Release/net462/PoeTweaks.Tests.exe --noresult --workers=0
Tests exercise XP calculations and nested target scopes, retention and recovery curve boundaries, monotonic peak tracking and native JSON persistence. They validate compiled game references and pass real Jotunn synchronization packets through independent server/client config instances. Initial packets and subsequent updates override client values without changing local-only settings, including under different number cultures. Tests do not run Valheim gameplay or a live client connection. Death-audio tests cover audience/radius boundaries, the local owned death transition, and compiled native audio/network references. Atlas tests cover JSON persistence, monotonic circle copies and warmup boundaries using the configured in-game day length. The isolated native startup check validates compatibility with ZenMap and invokes the actual patched table-read and map-input helpers. Headless checks do not verify the rendered client map. Inventory tests cover clean/stale comparisons, full signed item diffs, durable record replacement, account/world/character isolation and failed storage. Real native RPC/package roundtrips verify serialization and acknowledgement after storage; these do not replace a graphical client's actual Steam logout test.
Disk edits require restarting/reloading the mod; no file watcher is installed.
Editing an override requires the next ./setup.py import, not just a restart,
because the importer copies overrides into pack/.