You are viewing a potentially older version of this package. View all versions.
GBV-Hermod-0.1.2 icon

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
Date uploaded 2 weeks ago
Version 0.1.2
Download link GBV-Hermod-0.1.2.zip
Downloads 27
Dependency string GBV-Hermod-0.1.2

This mod requires the following mods to function

denikson-BepInExPack_Valheim-5.4.2350 icon
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.2350

README

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.

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, 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.

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.
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.

CHANGELOG

Changelog

0.1.5

Fixed

  • Commands that change a setting now say the change is live only and name hermod_save. Setting the send budget on a running server and losing it to the next restart, with nothing having warned you, is what prompted this
  • hermod_queue no longer says "no restart needed", which was true about when the change applies and read as "this is saved"

Changed

  • The portal ghost detector reports stale records as information rather than warning about a bug. It was firing on players simply walking away from each other, over a thousand times a session, claiming an object was left standing when nothing was. The count stays in the ghosts column

0.1.4

Diagnostics only. Nothing about how the server runs has changed.

Added

  • Slow frames now say whether the garbage collector was running during them. New gc_frames, slow_gc, slow_unexplained and gc_stall_ms columns, and the worst-frame list in the report tags them gc instead of leaving the cause blank
  • A breakdown of what is being destroyed, by prefab, in dead_created and dead_top. The session log already counted dead objects; this says what they were

Notes

  • gc_stall_ms is milliseconds out of the 60,000 in a minute, so it reads directly as how much of the server the collector took
  • slow_unexplained counts slow frames with no collection and nothing Hermod instruments running. If that is the large number, the time is going somewhere this mod cannot see: another mod's update loop, or the engine itself
  • Valheim's Mono runs a non-generational collector, so gc0, gc1 and gc2 are the same number printed three times and every collection walks the whole heap. Do not read them as a generational breakdown

0.1.3

Added

  • A portal ghost detector. It reports anyone left standing at a portal they walked through, by name, and counts it in a ghosts column. It does not fix them
  • fixZdoExtraDataLeak, off by default, for a leak in the game itself. Objects near a player who has not received them yet are mistaken for save copies and never freed
  • A log line at startup saying which backend the server opened on

Changed

  • Compression needs 0.1.3 on both ends. A player on 0.1.2 is not broken, they just exchange uncompressed packets

Notes

  • A ghosts column that stays at zero means your server does not have the problem. The log line also says how many players it is watching, so a zero is not the same as not looking
  • Watch extradata_ratio before and after turning the leak fix on. It will not fall, it should stop climbing
  • Compression and the Steam settings do nothing on a crossplay server, because they live on a Steam socket a PlayFab server never creates. That is what the backend line is for

0.1.2

First release.

Added

  • Packet compression. 8.6 MB of outgoing traffic became 3.0 MB on a real server, with no measurable change to frame time or CPU. Needs the mod on both ends
  • An adjustable limit on world data in flight per player. The game's own limit tightens with distance, so it bites hardest on the players furthest away
  • A budget for time spent building terrain, so several zones landing in one tick cannot stall the server
  • Nagle batching off and the Steam send rate ceiling raised, only ever in the direction that helps
  • A per-minute record to CSV: frame times, CPU, memory, object counts, per-player ping, bandwidth and update rate, compression, terrain generation and saves
  • An update rewrite that sends to every player on a timer rather than one per frame. Off by default
  • hermod_panic, which reverts every change in one command with players still connected

Notes

  • The frame rate is deliberately left uncapped. A Valheim server sends updates to one player per frame, so capping it would cut every player's update rate. A cap that would push delivery below ten a second is refused
  • Commands are typed from a client that has the mod, because a Valheim dedicated server has no console of its own
  • Crossplay servers get the measurement but not compression or the Steam settings
  • Run one networking mod, not two. If another is detected this one stands down from everything that overlaps and says so in the log