
VikingStoryteller
In-game F8 stats, skills and player snapshots, damage shares, AFK, native game counters and searchable story logs. Optional local HTML overview. No admin powers or automatic chat.Viking Storyteller
Experimental 0.4.0, built and tested against Valheim 1.0.15. Keep a factual record of your Vikings' adventures: kills, deaths, damage contributions, assists, skills, building, crafting, gathering, AFK estimates, biome crossings and exact-creature revenge.
Press F8 in game to open your own server statistics. The package also includes optional tools for a searchable local HTML overview and structured logs for humans and analysis tools.
Storyteller does not write roasts or send automatic chat messages. It supplies the facts for recaps. It grants no admin powers and does not change inventory, loot, combat outcomes or network ownership.
Install and update
Install the same version on the server and participating clients, then restart them. Dependencies remain BepInExPack Valheim 5.4.2350 and JsonDotNET 13.0.4. No additional UI mod or Jotunn dependency is required.
Clients without Storyteller can still join. Version 0.1 clients provide deaths; 0.2 clients provide enhanced combat/activity/action reports. A 0.4 client negotiates 0.4, then falls back through 0.3, enhanced 0.2 and death-only 0.1. The new stats window, native counters and snapshots require a 0.3 server. Missing collectors receive at most four negotiation attempts; reporting requires acceptance.
The update does not reconstruct unrecorded history. The in-game window reads the cumulative stats file introduced in 0.2; upgrading directly from 0.1 does not backfill that file from old death logs. The HTML tools can still search and summarize retained 0.1 events. Current skill levels are a snapshot, not levels earned on this server. Keep your existing world and Storyteller files when updating normally.
In-game stats window
F8 opens the window; F8, Escape or Close dismisses it. The hotkey is configurable if another mod already uses it. The window blocks normal movement/attack/look input while open and restores control on close. It refreshes from the collector about every 15 seconds while open, with a manual refresh button.
- Combat: creature killing blows, deaths, assists, player killing blows, actual damage dealt/taken and observed health gains.
- Adventure: active/AFK/unobserved time, placed pieces, emotes and searchable native/item counters.
- Skills & Condition: last observed biome, health, stamina, eitr, armor, weight, comfort, weapon, food and skill levels.
These are your world's accumulated collected records, not a live character sheet or necessarily your entire character history. Missing coverage says “Not collected yet.” Snapshots display their timestamp. The server chooses the account from the authenticated connection; a client cannot select another player's account or execute commands through this interface. Up to 384 metric rows are returned, with additional entries retained in server records.
Ten-minute work summaries
Version 0.4 groups routine work into one summary per reporting player approximately every ten minutes, plus partial summaries when they disconnect or the collector stops. Kills, deaths, revenge, emotes and biome transitions stay individual story events. There are no automatic chat messages.
- Building uses completed placements, with their actual positions and piece names.
- Mining and woodcutting use the game's hit counters, not ore yielded or trees felled. Crafting, upgrades, harvesting and fishing use their native action counters.
- Work positions are captured when the action occurs. Separate 25-metre 3D cells retain separate work sites; map coordinates are X/Z, with Y available in JSON. Mining/tree positions describe the player, not the exact resource object.
- The main activity uses distinct 30-second bins containing work evidence. It never compares a hundred pickaxe hits directly with two buildings or claims exact minutes spent. Tied categories are mixed. It describes observed work, not combat/travel dominance across all gameplay.
- Combat can link work observed in the preceding 60 seconds and, for deaths, within 35 metres. This says recent work nearby, not proof someone was attacked while building. Delayed work batches can leave this link absent.
- Summaries show protocol/reporting coverage, partial windows, active/AFK/unknown time and references to combat events. Referenced kills/deaths must not be added again to individual event counts.
Clients batch work about every 15 seconds; only story output is grouped by ten minutes. Windows use server receipt times; a delayed batch can span a boundary. Pending windows checkpoint every 15 seconds and recover as partial summaries after an unexpected restart. Up to the last checkpoint interval can be lost on a crash. Stable summary IDs allow deduplication after replayed recovery. Bounded buffers can drop evidence under sustained overload, reported in status.
F8's Adventure tab shows your last completed work summary and up to twelve sites. The HTML overview includes all retained sites, links to combat and searchable underlying evidence. Detailed work requires 0.4 on both sides; an older client with no work reports is not labelled idle.
Native gameplay statistics
Observe changes to all 205 actual PlayerStatType categories in the tested build: crafting/upgrades, mining, woodcutting, gathering, food, fishing, farming, taming, pets, building, travel, sailing, portals, boss credit and more. Named dictionaries retain what was picked up, crafted, harvested, eaten or placed. Only the currently connected local character profile is observed.
Counters are read before/after completed game methods and aggregated into bounded batches, normally every 15 seconds. Imported totals never become new progress. A respawn retains the same profile's pending progress; profile, connection and world changes clear it. Native death/kill categories can overlap the independently observed combat records: do not add them together. Increment and set changes remain separate, including decreases/resets.
Native callbacks can miss actions from mods that bypass those game methods. Counters use the game's units. Limited dictionary/batch sizes and backpressure favor gameplay over collecting every event. If optional native hooks fail, combat reporting remains enabled and status reports the missing feature.
Snapshots, skills, sessions and biomes
Snapshots normally arrive every 30 seconds. They include current health/max health, stamina, eitr, armor, carry/max weight, comfort, equipped weapon/quality, food and remaining duration, up to 128 skills, position, biome and sampled movement/condition flags. This is not a full inventory dump and does not inspect Epic Loot item custom data.
The client checks its biome every two seconds and can send an earlier snapshot when it changes. Very brief crossings can be missed. A biome transition does not establish travel method or the exact crossing instant.
Skill changes compare nearby samples of the same character within one connection. The first sample, a different character, clock rollback or a gap over 120 seconds starts a baseline. Newly seen skills require a baseline too. Gains/losses are observed differences; Storyteller does not guess whether a death, command or another mod caused them.
Session events describe when the collector observed a ready reporting connection and its end. Session duration includes AFK and excludes pre-negotiation time; it is not active play time. An abrupt process failure may leave a session without an end record.
Combat contributions and revenge
Each character's network owner observes resulting health changes. Damage measures HP actually removed after mitigation, caps overkill at remaining HP and ignores zero-loss hits. Each player/creature death can contain HP loss, hit count and percentage by source, with a separate killing blow. Unknown attackers and environmental causes remain unknown or explicit cause buckets; damage-over-time attribution only follows identifying game data.
An encounter resets on observed full recovery or after 120 seconds without recorded health loss. Partial healing can make total HP lost exceed maximum health. Percentages use observed encounter damage, not guaranteed complete damage or a causal percentage of blame. Health continuity gaps and out-of-order reports are flagged. Missing owners, restarts, queue limits or mods bypassing observed methods can leave gaps. In-progress encounters do not survive a restart.
An assist means recorded damage in the creature's final observed encounter without its killing blow; each account receives at most one assist per creature, including across respawns. Exact-creature revenge links require matching world and entity IDs, not just the same creature species. Health gains can include food and other HP changes, not just healing abilities.
AFK, emotes, building and removals
AFK defaults to five minutes without observed in-game input. The grace period counts as active. Activity is reported about every 15 seconds; long stalled/suspended intervals are explicitly unobserved rather than assigned to active or AFK time. This is a heuristic, not proof of physical presence. There are no kicks.
The client reads input-activity timing and focused controller activity, never key contents or chat text. Emotes count as activity. Incoming damage and boat movement do not prove someone is present.
Successful emotes and completed Player.PlacePiece calls record the local player, action, time and position. The server also records recognized boats and built structures when their network objects are permanently removed. Removal cannot distinguish destruction, dismantling or admin deletion, and does not identify a culprit. Ordinary client unloading is not permanent removal. Unrecognized/custom object types can be absent.
Server files
Under BepInEx/config/VikingStoryteller/:
README.mdandschema-v4.json: starting points and field meanings, including supported native statistics.<world-id>-events.jsonl: one versioned event per line, with stable IDs, UTC observation time, game time, source evidence and links.<world-id>-detail.jsonl: routine evidence: individual placements, health, metrics, work samples, snapshots and activity. Search here when investigating a summary.<world-id>-work.json: pending work-window checkpoint.<world-id>-state.json: compact death/revenge correlation and deduplication.<world-id>-stats.json: cumulative per-player totals and latest snapshots.status.json: version, feature readiness, protocol coverage, accepted/rejected/dropped counts and queues.
Events flush about every two seconds. State saves about every 15 seconds and on normal unload. Each event/detail file rotates near 20 MiB and retains ten archives per world. Crashes can lose buffered records, and old rotated files can leave history incomplete. Raw local records contain platform account identifiers, names, in-game locations and gameplay; do not publish them.
Optional local HTML and search tools
The tools/ folder contains dependency-free Python 3.10+ tools. These are run separately by the operator; the mod does not launch a web server. Point --server at an ordinary dedicated-server folder, the collector folder itself, or a managed folder containing server/BepInEx/.... Use your numeric world ID from the log filenames.
python data_hub.py --server "/path/to/valheim-server" --world WORLD_ID --output "/path/to/story-reports"
python preview.py --server "/path/to/valheim-server" --world WORLD_ID --output "/path/to/story-reports" --port 8766
The first command creates overview.html, summary.json, report.json, events.jsonl, an indexed events.sqlite, UTC day partitions, per-player exports and schema.json. The HTML is self-contained. The second optionally serves it on 127.0.0.1 only; open http://127.0.0.1:8766/ on the same computer and use Refresh data. No external scripts, analytics or fonts are loaded, and the service has no admin endpoints.
Filter by period, player, event type and text; drill into damage percentages, source lines, skill snapshots and exact-event links. Routine telemetry can be hidden from the timeline. Matching client/server biome observations remain as evidence but count once. The optional managed-server observer is read only when its local maintenance/admin/stories folder exists.
python query.py --database "/path/to/story-reports/events.sqlite" --player "Your Viking" --kind player_death --limit 20
python query.py --database "/path/to/story-reports/events.sqlite" --text Lox --since 2026-09-20T00:00:00-05:00
Derived exports omit platform account IDs and session tokens, retain source file/line references, and distinguish unavailable values from zero. UTC observation time is the query clock; the browser displays local time. Exports reflect retained input files; old day files may remain after rebuilding. Source logs remain untouched. Gameplay exports are private and are not included in this package.
Configuration
JacobsValheim.VikingStoryteller.cfg preserves existing master switches:
Send gameplay death reports: master client reporting/negotiation switch, despite its historical name.Collect gameplay death reports: master server collection and own-stats service switch.AFK after seconds without input: default 300, range 60–3600.Report player snapshots: default enabled.Player snapshot interval seconds: default 30, range 15–300; observed biome changes can report sooner.Open stats hotkey: default F8.
Restart the affected game/server after editing configuration. No RCON, API key, external analytics or admin powers are added. The optional Python preview opens only its explicitly requested local port.
Validation and limits
129 C# model checks and 13 reporting tests cover identity, damage math, correlation, AFK, snapshots, native deltas, respawn/profile boundaries, bounded own-stat views, provenance, SQLite search and missing-data handling. Isolated native-game tests exercise real damage/death/removal, patched profile increments, protocol v1/v2/v3/v4 negotiation, serialization, own-stats replies, input gating, and rejection of wrong identities, tokens, replayed or invalid reports. The HTML includes grouped work and detailed evidence.
Normal multi-PC play and visual/input testing of the new in-game window remain necessary. Headless native tests verify its hooks and server data path, not the rendered client window. This is an experimental release; missing or conflicting mods can leave incomplete reports. The server validates identity, world, session token, timestamp, ownership and payload bounds, but client observations are not anti-cheat proof.
Correlation retains 2,000 deaths and up to 8,192 victim IDs. Up to 2,048 encounters and 64 source buckets per encounter are retained. Native buffers cap at 512 names, cumulative native metrics at 4,096 names per player. World rollback can reuse IDs: preserve a backup, then archive/reset corresponding Storyteller state if you deliberately want a fresh reporting history.
MIT source and a Windows build script are included. Build against your own Valheim/BepInEx/Harmony assemblies. Game assemblies and private test/admin tools are not redistributed.

