
VikingStoryteller
Read-only gameplay stories: damage shares, kills, assists, AFK estimates, emotes, building and boat removals. Server and client reporting; no admin powers or auto chat.Viking Storyteller
Experimental 0.2.0 for Valheim 1.0.15. Records gameplay facts for per-player recaps: deaths, killing blows, damage contributions, assists, AFK estimates, emotes, building placements, and boat/structure removals. Exact-creature revenge links are retained.
The mod does not generate prose or post automatic chat messages. It provides local files for a server operator or reporting tools. It grants no admin powers and does not change combat, inventory, loot, building or multiplayer ownership.
Install and update
Install on the server and all participating players' Thunderstore profiles, then restart the server and games. Dependencies: BepInExPack Valheim 5.4.2350 and JsonDotNET 13.0.4.
Clients without this mod can still join. Version 0.1 clients still supply deaths to a 0.2 collector, but cannot supply new observations. A 0.2 client falls back to death-only reporting with a 0.1 collector. A server without the mod receives at most three negotiation attempts; continuous reporting requires acceptance.
Damage contributions and statistics
The owner of each character observes resulting health changes. Actual HP lost is measured after armor, resistance and other changes handled before the final health write. A finishing hit receives credit only for HP actually removed, not its overkill attack value. A fully blocked hit with no HP loss contributes zero.
Both player and creature deaths receive an encounter breakdown. Each source includes an entity identity when available, HP removed, hit count and percentage. The killing blow remains separate. Unknown attackers are never assigned to a guessed player or creature. Falls and other unattributed causes retain separate buckets. Damage-over-time effects are attributed only when the game's hit data identifies their attacker.
An encounter resets at observed full recovery or after 120 seconds without recorded health loss. It can continue throughout a long boss fight while damage continues. Partial healing remains in the encounter, so total HP lost can exceed maximum HP. Percentages divide each source's observed HP loss by total observed HP loss, including unknown sources. They describe recorded damage, not guaranteed complete coverage or a causal percentage of blame. Starting health and coverage notes accompany the breakdown.
Accepted observations accumulate on the collector across ownership changes. A report can still be rejected if its ownership evidence has vanished. Missing reporters, restarts, queue overflow, unknown prefabs and mods bypassing observed methods can leave gaps. In-progress encounters do not survive a collector restart. A death with no damage observations says unavailable rather than inventing a breakdown.
Per-player totals include creature killing blows, player killing blows, creature assists, deaths, HP damage dealt to creatures/players, HP lost, observed health gains, active/AFK seconds, emotes and pieces placed. An assist means positive recorded damage in that creature's final recorded encounter without its killing blow. Each account gets at most one assist per creature, including across respawns. Historical deaths are not retroactively converted into damage or assist statistics. Health gains can include food or other HP changes, not just healing abilities.
AFK, emotes and building
AFK defaults to five minutes without observed in-game input. The grace period counts as active; only time after it counts AFK. Activity reports are sent about every 15 seconds, giving transition timestamps that resolution. Unreported time, disconnects and long stalls are not silently counted as either active or AFK.
The client reads Valheim's input activity timestamp and controller stick activity while focused. It sends durations and an AFK flag, never key contents, chat text or device identifiers. Successful emotes also count as activity. Boat movement and incoming damage do not. This is a heuristic, not proof of physical presence; scripts, held inputs and controller drift can affect it. There are no AFK kicks or announcements.
Successful local-player emotes record name, player, time and position. Completed Player.PlacePiece calls record the prefab and placement position. This includes mods using that method; it is not an inventory or crafting log. Failed or skipped methods are not reported.
The server separately records confirmed permanent network-object removals for recognized boats and built structure prefabs. boat_removed and structure_removed include prefab, object identity, time and position. Removal does not distinguish destruction, dismantling or admin deletion and does not identify who caused it. Ordinary client unloading is not permanent network-object removal. Custom boats without the game's Ship component and unrecognized prefabs may not be classified.
Biome/travel observation, item collection, crafting, equipment changes and chat transcription are not provided by this release. External server observers can supplement these files.
Files and reporting
Under BepInEx/config/VikingStoryteller/:
<world-id>-events.jsonl: deaths, revenge links, health changes, activity, emotes, placements and removals.<world-id>-state.json: death deduplication and correlation.<world-id>-stats.json: cumulative per-player statistics since enhanced collection began, keyed by account rather than display name.status.json: collector health, protocol coverage and accepted/rejected/dropped counts.
The event file rotates near 20 MiB and retains ten archives per world. Logs flush every two seconds; state saves every 15 seconds and on normal unload. Crashes can lose buffered records. Event IDs support deduplication. Reports can be incomplete if older logs have rotated away.
The optional read-only Python 3 tool tools/report.py produces per-player JSON and a timeline for a time range. It omits account IDs from its output. Example:
python report.py --directory /path/to/BepInEx/config/VikingStoryteller --world YOUR_NUMERIC_WORLD_ID --since 2026-09-20T12:00:00-05:00
No extra port, API key or external analytics service is used. This package includes the DLL, documentation, license, source and report tool; no player logs, admin helper or test plugin. Local server records contain account identifiers, in-game names, positions and gameplay events. Tell players what is collected and avoid publishing raw files.
Configuration: JacobsValheim.VikingStoryteller.cfg. Existing Send gameplay death reports and Collect gameplay death reports switches now cover all reporting, preserving prior opt-outs. AFK after seconds without input defaults to 300 and accepts 60 through 3600. Restart after changing configuration.
Validation and limitations
52 pure-model checks cover damage math, identity, deduplication, attribution, AFK and action validation. An isolated Valheim test covers real HP changes/death, capped overkill, healing, boat/structure removal and actual ZRpc negotiation/serialization. Protocol tests reject wrong owners, invalid tokens, replayed sequences, oversized batches and another player's activity claims. Normal multi-PC testing of client input, emotes and placement remains necessary.
The collector validates peer identity, session token, world, timestamp, numeric/payload bounds and victim ownership. Client observations are not anti-cheat proof. No reporting RPC executes commands. Bounded queues, rate limits, source limits and log rotation favor gameplay over retaining every observation under overload.
Correlation retains 2,000 deaths and at most 8,192 victim IDs. Up to 2,048 encounters and 64 source buckets per encounter are retained; excess sources enter a labeled bucket. World rollback can reuse IDs: archive/reset matching story state after deliberately restoring a world if a fresh history is desired.
MIT-licensed source is included. Build against Valheim managed assemblies, BepInEx, Harmony and JsonDotNET. Do not redistribute game assemblies.

