Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Hermod
Compresses about two thirds of a dedicated server's outgoing traffic away, makes the ZDO send budget adjustable, and measures where the server's time actually goes. Compression needs the mod on each player too.
By GBV
| Last updated | 15 hours ago |
| Total downloads | 12 |
| Total rating | 1 |
| Categories | Mods Tweaks Deep North Update |
| Dependency string | GBV-Hermod-0.1.3 |
| Dependants | 0 other packages depend on this package |
This mod requires the following mods to function
denikson-BepInExPack_Valheim
BepInEx pack for Valheim. Preconfigured with the correct entry point for mods and preferred defaults for the community.
Preferred version: 5.4.2350README
Hermod
Performance and networking for Valheim dedicated servers. No gameplay changes.
It compresses most of the server's outgoing traffic away, makes two limits the game hardcodes adjustable, and writes a per-minute record of where the server's time actually goes.
Named for Hermod, the swift messenger of the gods, who rode nine nights down to Hel and back to carry word - which is a dedicated server's whole job.
What it does
Packet compression, measured at a 2.8x reduction. On a real server 8.6 MB of outgoing traffic became 3.0 MB, holding around 36% while a player was moving, with no change to frame time or CPU. It uses Zstandard with a dictionary trained on real Valheim traffic, which is what makes it work - the packets are small, and generic compression on a two kilobyte buffer has almost nothing to learn from.
Compression is negotiated per player and needs the mod on both ends. A player without it simply exchanges ordinary packets.
An adjustable ZDO send budget. The game stops sending a player world data once 10240 bytes
are in flight to them, and that count includes bytes already on the wire awaiting
acknowledgement - so it is really a throughput cap that tightens with distance, roughly 82 KB/s
at 100 ms of latency and 33 KB/s at 250 ms. hermod_queue 32768 took the 95th-percentile
packet from 16 KB to 64 KB on the test server.
Raise it for a reason, not by taste. Every message shares one ordered stream, so a bigger backlog delays chat and player positions too.
A zone generation budget. ZoneSystem.Update asks for a new zone once for the server plus
once per player and discards every result, so one 100 ms tick can generate several zones. Zone
generation is the largest single cost on a dedicated server - the worst frame measured was
1493 ms, of which 1481 ms was one location being built. The budget caps how much of a tick goes
into it.
Nagle off, and the Steam send rate ceiling raised. The game pins Steam's minimum and maximum send rate to the same number, which switches Steam's own rate estimator off. Hermod leaves the minimum exactly where the game had it and raises only the maximum, so the result is never less than before.
The frame rate is deliberately not capped. The server sends world updates to one player per
frame behind a 0.05 second gate, so each player's update rate is 1 / (0.05 + players/fps).
Capping a six-player server to 60 fps cuts that from 17.9 a second to 6.7. Hermod leaves it
alone and refuses a cap that would push delivery below ten a second for the number of people
online.
A ZDO relay rewrite, off by default. The same one-player-per-frame loop means a server that slows down delivers updates more slowly. The relay drives it from a clock instead and serves every player on each pass. It ships off because the case it most improves is a player with real latency; turn it on with numbers from your own server. If frame times get materially worse it turns itself off.
A portal ghost detector. When a player goes through a portal, the server is supposed to tell
everyone else to drop the copy of them standing at the old one. Hermod watches whether that
actually happened: a player who moved far enough to have teleported, and any other player still
holding their character record while they are outside that player's active area, is a client
showing a ghost. It is reported in the log by name and counted in the ghosts column.
It does not fix them. Nothing in Hermod changes how the game behaves until there are numbers
saying it should, and a fix for this would be a gameplay change. If ghosts stays at zero on
your server, there is nothing here to fix.
Installing
Install it on the server. Install it on players too if you want compression, which needs both ends - everything else is server-side.
The commands also need it on the client you type them from: a Valheim dedicated server has no console input, so commands travel to the server over the wire and are checked for admin rights before they run.
What it writes
Three files in BepInEx/config/:
| File | Contents |
|---|---|
gbv.valheim.hermod.session.csv |
One row a minute: frame times, CPU, memory, object counts, send rates, compression, zone generation, portal ghosts, saves. |
gbv.valheim.hermod.peers.csv |
One row a minute per player: ping, link quality, bandwidth, queue depth, update interval. |
gbv.valheim.hermod.report.json |
The session, with the worst frames and slowest zone generations timestamped. |
A short summary also goes to the BepInEx log every few minutes.
Percentile columns are histogram bucket edges, so ms_p99 = 16.7 means 99% of frames were at or
below 16.7 ms. The _max columns are exact.
teleports counts players who moved more than 64 metres between samples, which is a portal hop
rather than running. ghosts is the worst count seen in any one sample that minute, not a total:
the same ghost persists across samples, so adding them up would measure how long the server ran
rather than how bad the problem is. The log summary also states how many players it is watching
and the furthest anyone moved, so a zero can be read as "nothing wrong" rather than "not
looking".
extradata_ratio climbing while zdo_total stays flat is the leak fixZdoExtraDataLeak closes.
Commands
| Command | What it does |
|---|---|
hermod_status |
Frame time, CPU, object counts, compression and send rate right now. |
hermod_peers |
Per player: ping, queue depth, how often they are updated. |
hermod_mark <label> |
Stamp a labelled boundary into the logs. |
hermod_profile <vanilla|tier1|relay> |
Flip the whole set of settings at once, live. |
hermod_compress <on|off|status> |
Packet compression. Applies immediately for everyone connected. |
hermod_queue <bytes|off|status> |
The per-player ZDO send budget. |
hermod_relay <on|off|status> |
The ZDO relay. |
hermod_net [apply] |
Show the Steam settings in force; apply re-asserts them on live connections. |
hermod_fps <n|auto|off> |
Cap the frame rate, with the refusal described above. |
hermod_probe <on|off> |
The detailed timing patches, leaving the rest of the measurement running. |
hermod_report |
Write the JSON report now. |
hermod_reload |
Re-read the settings file. Never starts the relay. |
hermod_save |
Write the settings currently in force back to the file. |
hermod_panic |
Put everything back the way the game had it, immediately. |
Settings
BepInEx/config/gbv.valheim.hermod.json. The two switches in the BepInEx config are Enabled
and MeasureOnly.
| Setting | Default | What it does |
|---|---|---|
compressionEnabled |
on | Packet compression. Needs the mod on both ends. |
compressionLevel |
1 | Zstandard level, 1 to 9. Higher buys a few percent for several times the CPU, per packet, on the main thread. |
inFlightCeiling |
0 | The per-player ZDO send budget. 0 keeps the game's 10240. |
zoneGenBudgetMs |
50 | Milliseconds of each 100 ms tick that may go into generating zones. 0 removes the limit. |
applySteamSettings |
on | Master switch for the socket changes. |
nagleTimeMicros |
0 | Steam's Nagle delay. 0 disables it, -1 leaves it alone. |
sendRateMin |
153600 | Left at the game's own value. |
sendRateMax |
1048576 | The ceiling Steam is allowed to reach. |
sendBufferSize |
0 | Leaves Steam's default alone. |
maxTotalUploadKiBPerSec |
0 | Your upstream budget, divided between the player slots. |
targetFrameRate |
0 | 0 auto, -1 uncapped, or a number. Auto means do not cap. |
relayFrameRateCap |
60 | The cap auto chooses once the relay is running. |
relayEnabled |
off | The ZDO relay. |
relayTargetHz |
20 | Passes per second it aims for. Cannot exceed the frame rate. |
relayAbortFrameMs |
250 | How bad frame times must get before the relay turns itself off. |
fixZdoExtraDataLeak |
off | Closes a leak in the game itself. ZDO.SaveClone shares a field with the send-ordering key, so any object within 150 m of a player who has not yet received it is left marked as a save clone and its data is never freed. Watch extradata_ratio climb with this off, then turn it on. |
probesEnabled |
on | The detailed timing patches. |
reportMinutes |
5 | How often the report and the log summary are written. |
slowFrameMs |
100 | A frame worse than this is kept verbatim in the report. |
slowZoneGenMs |
50 | A zone slower than this is kept verbatim, with its coordinates. |
writeCsv |
on | Write the two session CSVs. |
writeJson |
on | Write the JSON report. |
csvMaxBytes |
8388608 | Rotate the CSVs past this size. |
deferToOtherNetworkMods |
on | Stand down if another networking mod is installed. |
betterNetworkingGuid |
DIT.BetterNetworking10 |
Which plugin id to stand down for. A substring match catches forks. |
A config file that will not parse leaves whatever was already in force alone, rather than reading as empty.
The flags column in the session log records what was live each minute: N Nagle, R send
rate, F frame cap, Z relay, C compression, Q send budget raised, G zone budget.
Running alongside other mods
Run one networking mod, not two. If Hermod detects another it stands down from the send rate, the Nagle delay, the relay and compression and says so in the log; measurement carries on. Two mods framing the same packet would produce corrupt traffic rather than a clean failure, which is why the stand-down is automatic.
If a Valheim update moves one of the game internals Hermod hooks, it names it in the log and
carries on with everything that does not need it. Reading the log for Hermod: once after an
update is worth doing.
Credits
Built for Guys Being Vikings.
Packet compression is adapted from CW-Jesse's BetterNetworking, by way of LabodiDavid's
Valheim 1.0 fork, under the MIT licence - including the Zstandard dictionary trained on real
Valheim traffic, which is the part that makes compression worth doing. Full notice in
THIRD-PARTY-NOTICES.md, licence in LICENSE-BetterNetworking.txt.
Zstandard itself comes from ZstdSharp.Port by Oleg Stepanischev.
Everything else was written from Valheim's own decompiled code.