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.
GsValheimStatsEmitter
Server-side companion for the gs dashboard: world milestones, per-player boss damage, live online players + in-game day, and server-owned combat. Pairs with the gs client companion.
| Last updated | a week ago |
| Total downloads | 81 |
| Total rating | 0 |
| Categories | Misc Server-side |
| Dependency string | Proudlock_Technology-GsValheimStatsEmitter-0.2.4 |
| 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.2333README
GsValheimStatsEmitter (server-side)
A dedicated-server mod that posts the stats only the server can see to an HTTP endpoint - by default the self-hosted gs dashboard, but it'll POST to any URL.
Pairs with the GsValheimStatsClient companion (which each player installs for their personal stats). Both POST to the same endpoint; a receiver merges them by world + player name.
Full payload schema, key conventions, and merge rules are documented on the Data format & self-hosting wiki.
Why a server mod at all? Valheim hands creature simulation to whichever client is nearest, so most combat is captured client-side. The server still owns idle / distant creatures (e.g. a sleeping mob you sneak-attack), so this mod captures those - notably the true post-backstab hardest hit - plus world-progression milestones the server is authoritative for.
What it tracks
- World milestones ("quests") - boss defeats, Hildir's Request bounties, first troll/surtling, etc. (from the world's global keys)
- Per-player boss damage for bosses the server owns
- Weapon damage + hardest hit for server-owned creatures (the sleeping-backstab case)
- Live presence - connected players + in-game day (
onlinePlayers/worldDay), force-emitted on join/leave for near-instant "online now" - Active mod list + world identity (name, host, uptime)
Setup
-
Install on your dedicated server (depends on
denikson-BepInExPack_Valheim). Drop the DLL inBepInEx/plugins/. -
Launch once to generate
BepInEx/config/net.cproudlock.gsvalheimstats.cfg, then set:[General] World = vhserver3 # your server's -world name EmitIntervalSeconds = 120 [Ingest] Url = http://localhost:3001/api/valheim/ingest # dashboard reachable from the server Token = <bearer token> -
Restart the server. Cumulative counters persist to
BepInEx/config/<guid>.state.tsv.
Ingesting the stats yourself
Same contract as the client mod - an authenticated POST of a JSON snapshot (see that mod's README for a minimal receiver + curl test).
Payload (source: "server"):
{
"schemaVersion": 1,
"game": "valheim",
"source": "server",
"world": "vhserver3",
"hostName": "Proudlock VH Server 3",
"serverStartedAtUtc": "2026-06-09T11:32:00Z",
"onlinePlayers": ["Erebus", "Pridetoes"],
"worldDay": 12,
"emittedAtUtc": "2026-06-09T18:00:00Z",
"snapshotIdLocal": "9a6b...",
"players": [{
"name": "Erebus",
"boss": [{ "boss": "Eikthyr", "kills": 1, "damageDealt": 1450, "fightSec": 95 }],
"weapons": [{ "weapon": "Knives", "damageDealt": 80, "kills": 2, "hardestHit": 79 }]
}],
"bossKillEvents": [{ "boss": "Eikthyr", "fightSec": 95, "firstBlood": "Erebus", "topDamagePlayer": "Erebus", "topDamage": 1450, "participants": 2, "tsUtc": "..." }],
"milestones": [{ "key": "defeated_eikthyr", "label": "Eikthyr defeated", "kind": "boss", "tsUtc": "..." }],
"mods": [{ "guid": "Azumatt.AzuCraftyBoxes", "name": "AzuCraftyBoxes", "version": "1.8.14", "author": "Azumatt", "folder": "Azumatt-AzuCraftyBoxes-1.8.14" }]
}
Merging client + server feeds
Each feed reports the creatures it simulates, so they're disjoint. A receiver should:
- sum
damageDealt/killsper(player, weapon)across the twosources (exact total, no double-count, since a single hit is only ever processed by one side), and - take the max of
hardestHit.
The gs dashboard does this with per-source rows + SUM / MAX(...) at read time.