Raven Iron
All Devlogs
UpdateRagnarok's Wrath

Ragnarok's Wrath v0.24.0: A World That Remembers, and the Storm That Sets It Alight

By RavenIron Games

Ragnarok’s Wrath is live: a server-authoritative world simulation for Valheim where fire leaves scars, plague creeps through the ground, soil tires, and every zone quietly keeps score of what happened there — across restarts, forever.

There is no UI. That constraint shaped almost every engineering decision below.


1. Drift Belongs to a Coordinate, Not an Object

The obvious place to store “this ground is scorched” is a ZDO custom key. We rejected it, twice, for the same reason: a ZDO attaches to an object, and drift attaches to a coordinate.

Storing zone state on an object means the state lives or dies with that object. The workaround — spawning an anchor prefab per zone — walks straight into one of Valheim’s quieter landmines: ZNetScene.CreateObjectsSorted calls DestroyZDO on any prefab hash it cannot resolve. A client missing the mod does not merely fail to see our anchor; it deletes it. Silent, permanent data loss, visible only as “the land forgot.”

So drift lives in a world-scoped sparse file, keyed by world uid, in plain tab-separated text. Writes are atomic with a rotating .bak, and a file that parses as garbage is quarantined to .corrupt rather than overwritten by the next autosave — the evidence outlives the failure.

One trap worth naming for anyone writing sidecar files against Valheim saves:

// World.GetWorldSavePath() defaults to FileSource.Auto, which returns "" on a
// Steam Cloud world — cloud saves are addressed by a RELATIVE path through
// Steam's API. Any mod using plain File.* must ask for Local explicitly.
string dir = Utils.GetSaveDataPath(FileSource.Local);

That returned empty string reads exactly like a broken absolute path and is, in fact, a correct relative one.


2. The Clock That Keeps Running When Nobody Does

ZNet.UpdateNetTime returns early when zero players are connected. The world clock stops on an empty server — which means anything timestamp-clocked to it silently stops too.

The tempting fix is to patch the global clock so time keeps flowing. Do not. AwayFromHome credits offline production against that same clock; unfreezing it would double-credit every furnace and kiln on the server, and the mod that broke it would not be the mod that gets the bug report.

Instead, ZoneClock keeps its own real-UTC ledger and settles it on contact. Drift accrues only where players have actually been — and it works in both directions. A zone nobody has visited neither rots nor heals; it waits. This also happens to be the correct behaviour next to AwayFromHome, whose keeper rotation loads and unloads sites all day: if drift ticked on zone load state, the land would age based on whether someone built a stone nearby.


3. One Write Across a Mod Boundary

Ragnarok’s Wrath does not simulate fire. FireFront does, and the two are separate mods on purpose: the bridge between them reads FireFront’s active fires by reflection and banks the scorch. As of v0.23.0 that bridge carries exactly one write — storm lightning.

The design rule is the whole reason it stays clean: Ragnarok’s Wrath decides when and where; FireFront owns every consequence. We pick a point and call IgniteGroundNear. Spread, burn-down, extinguishing, VFX and arson attribution are none of our business from that instant on.

Every gate before that call is a real rule, ordered cheapest-first:

private void TryLightning()
{
    if (!ModConfig.StormLightningEnabled.Value) return;
    if (!WeatherSystem.StormActive) return;
    if (EnvMan.IsWet()) return;   // FireFront's own rain suppression, honoured up front

    float chance = LightningStrike.ChancePerTick(
        IntervalSeconds, ModConfig.LightningMeanMinutes.Value);
    if (_rng.NextDouble() > chance) return;

    // FireFront's fire event is global and its igniter is captured ONCE — a bolt
    // joining a player-lit event would bill that arsonist for the sky's scorch.
    // While an attributed fire burns, the sky holds its peace.
    if (TryReadIgniter() != 0) return;

    // ... a real player actually under the storm, then the homestead standoff
}

Two of those deserve a note.

The igniter check is an ethics gate, not a physics one. The land books arson against the player who lit it, by name, across a fire’s entire spread. FireFront captures the igniter once per fire event, so a natural bolt landing while a player-lit fire burns would inherit that player’s name and bill them for weather. The sky waits instead.

The homestead standoff fails closed. Any ZDO carrying a builder id — pieces and planted crops alike — within the standoff radius grounds the bolt, checked XZ-planar across the 3×3 sectors around the strike via vanilla’s own ZDOMan.FindSectorObjects. If the world cannot be checked at all, the bolt is lost, never risked. A blocked bolt is never rerolled, so the configured strike rate stays honest.

And when a bolt does land in rock or wet sand and nothing catches, the announcement still says lightning struck. That is honest weather. Whether fire catches is FireFront’s verdict, not ours.


Six strikes across three storms on a live dedicated server confirmed the chain end to end: dry-sky gate, ignition, spread, scorch banked in the store, and the rivalry ledger billing nobody.

One operational footnote for server owners: a forced wet storm look suppresses lightning through the rain gate by design. The dry storm is the one that strikes.

Correction, 2026-09-23: on a dedicated server, that rain gate did not work before Ragnarok’s Wrath 0.27.2. A headless server never sees the sky a storm forces on its clients, so lightning struck under wet storms as well as dry ones. From 0.27.2, when a storm look is forced, lightning follows the look the storm actually rolled, so a wet storm now holds its fire. Listen hosts and single-player worlds were never affected.

Ragnarok’s Wrath v0.24.0 is available now on the Ragnarok’s Wrath project page.