Confirm with your password

Undertow

The sea gets its own motion. Currents run in a learnable shape - basin drift, coastal set, fast water between islands, slack behind a headland - and carry what floats on them. Tides, storm surge, flotsam. No HUD. One DLL, server and clients.

· Website

Stars
5
Downloads
871
Version
1.0.3
Updated
Author
RavenIronStudios
Virus scan
✓ Scan successful
Runs on
Client & Server
Required by
4 mods

Description

Undertow

The sea remembers where it is going.

A Valheim mod by Raven Iron. Valheim's ocean already has weather — waves that answer the wind, storms that raise them, a hull that takes damage in a seaway. What it doesn't have is water. Nothing moves. A karve left at half sail on a fixed heading arrives exactly where geometry says it will, every time, in every part of the map.

Undertow gives the sea its own motion. Currents run across the ocean in a shape you can learn: a slow basin drift, a stream that follows the coast, fast water between close islands, and dead water behind a headland. They carry what floats on them.

No map, no HUD, no icons. The sea shows its own motion the way water does — foam lying along the flow, moving with it, glassy where it goes dead — and nothing reads it out for you.


What it does

🌊 The sea has a shape

A current field spread across the whole ocean, built from a stream function — so the flow is divergence-free, the way real water is. Gyres, races and slack water all fall out of the same mechanism rather than being placed by hand.

  • The drift — slow, basin-scale, the thing you plan a long voyage around.
  • Coastal set — a stream that follows the shore, with a slight push toward it. This is why you don't doze at the tiller with the coast downwind.
  • Races — water accelerates between close landmasses. A narrow gap is fast water, in one direction, and the constriction is read at two scales, so a wide strait registers as well as a gap between rocks.
  • Slack and eddies — where opposing arms meet, the water goes dead. Things collect there.

It is the same on every machine, and no water ever crosses the network: the field is a pure function of the world seed, the position, the world clock and the season, so two players a thousand metres apart compute the same sea without exchanging a byte of it. What they do have to agree on is the tuning, and since 0.8.0 the server publishes that (see On a server, below).

🌒 Tides

A slow flood and ebb on a configurable cycle. It swings how hard the open ocean runs and reverses the coastal stream, so the passage you know is a different passage six hours later. Departure time becomes a decision.

Watched through a cycle on Valheim 1.0.15: one coastal point read sixteen times from wake field as the tide ran — 0.585 m/s ESE at peak ebb, 0.272 m/s S at peak flood, the along-shore stream reversing under an open-water drift that never turns, and the peak-flood reading predicted before it was taken.

⛈ Storm surge (with Ragnarok's Wrath)

Where a Devastating Storm stands, the water rises — there and nowhere else. A sheltered passage stops being sheltered while the storm sits over it. Entirely optional: without Ragnarok's Wrath the bridge logs its absence once and the sea runs regardless.

⛵ Boats are carried, never braked

A hull under way — paddle or sail set — feels the current as drag relative to the water, which is what a current is: going into it costs you exactly the water's speed over the ground, going with it pays exactly that, whatever your hull, and your speed THROUGH the water is never touched. (Through 1.0.0 the current was a constant push against any hull driving upstream, which stopped a paddled karve in 0.2 m/s of water; 1.0.1 fixed it.) A boat left drifting settles at the water's own speed: a karve re-measured on Valheim 1.0.15 read 0.99–1.00 against the water's 1.0, with and without Njord and Sailing loaded. A karve and a longship measured together before Valheim 1.0 read 0.86 and 0.96 — two hulls that damp very differently, both near the water's speed, which is the point; the longship has not sailed again since. A hull also resists sideways drift more than forward drift, so a current on the beam moves you less than one off the bow — that falls out of Valheim's own per-hull physics rather than being imposed, and it differs from hull to hull.

Boats with nobody aboard are not touched, by default. Vanilla already damps an unmanned hull almost to a stop, and a moored longship wandering off while you are away is not a feature.

🪵 Flotsam

Currents converge, so things gather. Driftwood, cargo and what the drowned no longer need collect in slack water — giving you a second reason to know where the sea goes quiet.

Uses only vanilla items, never a new prefab. Capped, reclaimed on a timer, and spawned only near a real player — an empty ocean stays empty. Watched on a dedicated server on Valheim 1.0.15: twelve items spawned to the cap and the cap held; fourteen reclaims each landed at 1801 s against an 1800 s timer, including items five kilometres from the only player; the server's whole object table moved by exactly one per item; and ten minutes of empty server produced nothing and still cleaned up.

🏊 Swimmers

The current carries a swimming body too, gently. It is hard-capped below swim speed: you can always out-swim the water and reach shore. That is a safety property rather than a balance dial, and it is enforced across every setting the config permits. Seen engaged on Valheim 1.0.15: in a race running a full metre per second, with the drift factor at the top of its range, the swimmer's drift read 0.4 (cap 0.4) and a swimmer heading upstream made 1.57 m/s over the water — swim speed minus the cap, to the second decimal.

🌫 The sea shows its set (0.7.0)

Faint foam streaks lie along the current on the water itself, move at the water's own speed and ride the swell — thick in a race, sparse at a trickle, and thinning to almost nothing where the sea goes slack. (Through 0.7 slack water was bare; since 0.8 the code admits a scattering there — a few flecks at most, and easy to miss by eye — so wake lines in an empty-looking sea tells dead water from a feature that is not running. DriftLineSlackFloor = 0 restores the old contrast exactly.) From a drifting hull they hold station alongside; under sail they stream past at the crab angle, and that angle is the set. Nothing states it: no arrow, no number, no screen element. The streaks are symmetric end to end, so a glance gives you the line of the flow and only watching gives you the sense — which is how a sailor reads a tide.

Client-side and cosmetic. It changes nothing about how a boat or swimmer moves, is never networked or saved, and a dedicated server ignores it. Night, fog, distance and a big sea dim it on their own; how much of the day the foam keeps at midnight is DriftLineNightFloor (0.7 shipped, set by eye on black water). EnableDriftLines turns it off; section 7 - Drift lines tunes count, radius, opacity, the night floor and a per-frame cost budget the mod enforces on itself.


Install

Drop Undertow.dll into BepInEx/plugins/ on the server and every client. Boat physics runs on whichever machine owns the hull — a player's — so a server-only install pushes nothing.

Config appears at BepInEx/config/com.raveniron.undertow.cfg. Every system has its own switch; every rate, cap and threshold is tunable.

You do not have to keep the config identical on every machine. The field is recomputed independently on each one, so two machines with different tuning would sail different oceans — which is why the server publishes its gameplay tuning and every client sails by it for the session (see On a server, below). Section 7 - Drift lines and the other per-machine keys stay yours by nature: foam is drawn by the client that looks at it, so two players on one deck see the same set, density and speed from the same field, but not the same individual streaks.

Your settings survive an update. When a release changes a default, your file is read before anything binds, a copy lands beside it as .vN.bak before any value is touched, and only values still sitting at an old shipped default are moved. Anything you set yourself is kept, and named in the log so you can see it was read and left alone. A [0 - Meta] section stamps which layout your file was written for; leave it be. wake status reports it.

The wake console

Command Answers
wake status what this machine is, what is running on it, which config layout your file carries, and whether you are sailing the server's tuning or your own
wake here the current under your keel — speed, bearing, depth, tide
wake field <x> <z> the current anywhere, loaded or not
wake drift whether the current is actually reaching boats
wake floats which item prefabs carry Floating (the flotsam palette)
wake lines whether the current is showing on the water, and what it costs (wake lines reset rebuilds it)

Turn on VerboseLogging and the log carries a boot-time transect of your seed's ocean, plus a running readout of drift while you sail.

On a server: the server's sea wins

The current is a pure function of your world seed, so every machine works out the same water without a byte of traffic — as long as they agree about the tuning. If a server raised MaxCurrentSpeed and you did not, you and your crewmate were quietly sailing different oceans: nothing desyncs, nothing errors, and nobody can tell.

So the server publishes its thirteen gameplay dials — the five that shape the field, the Ragnarok's Wrath switch, and the drift and swimmer settings — and your client sails by those for that session only. Your config file is never written to. Leave the server and your own settings are exactly where you left them. Values arriving from a server are clamped to the range your build allows, so a server can make the sea faster and never impossible.

What stays yours. The drift lines and everything about them, the tick budget, the field refresh rate, verbose logging, and every flotsam setting. Those are your machine's frame rate, not the server's sea.

Admins can change it live. If you are an admin, editing a synced value on your client sends it to the server, which saves it to its own config and tells everyone. If you are not, the server decides — with vanilla's own admin check, not your client's word for it — and your edit stays in your own file.

Undertow does not have to be on a client at all — but a client without it computes no current for the hulls it owns, because drift is applied by whoever owns the boat.

Plays well with

  • Ragnarok's Wrath (Raven Iron) — detected automatically. Storms raise the sea where they stand and the season shifts the drift. Optional; absence is logged once and changes nothing else.
  • Seasonality / Seasons — never touched. Undertow selects no environment, reads no season directly, and modifies no material.
  • Njord (Wubarrk) and Sailing (Smoothbrain) — measured together on Valheim 1.0.15: a drifting karve settles at the same speed with them as without, and Njord's per-hull speed cap and the current coexist without a judder. Njord changes the hull's handling, Sailing its propulsion, Undertow the water.
  • Boat stat mods in general should compose for the same reason: they change the hull, Undertow changes the water.

What it deliberately is not

No map, no compass, no wind gauge, no HUD of any kind — the drift lines are the sea, not an instrument: nothing is drawn on the screen and nothing reads the current out. No new prefabs, assets or bundles. No second wave system and no weather — vanilla and Seasonality own that ground. No boat rebalancing and no new ship types. Undertow changes the sea and nothing else.


Raven Iron. See also Ragnarok's Wrath (the world reacts and remembers) and FireFront (fire that spreads).


Support Raven Iron

Every Raven Iron mod is free, and stays free — all of it, always. Nothing is held back for patrons, and nothing ever will be.

If you'd like to help cover server hosting and test hardware:

Changelog

1.0.3 Latest

Updated due to 1.0.16 Patch.

1.0.2

Fixes and cleanup. Nothing about the water or the drift changed, no config key moved, and the wire is the same, so 1.0.1 and 1.0.2 still share a sea.

  • Flotsam now washes up in single player and around a host's own player. Until now it only ever spawned near players connected to a server from elsewhere, so a single-player world never saw any, and on a hosted (non-dedicated) game only the guests did. The host's own player now counts like everyone else. Dedicated servers are unchanged, and an ocean with nobody in it still stays empty. A single-player world can now hold up to FlotsamMaxAlive pieces when you quit; they are vanilla items and vanilla's own despawn clears them.

  • The server now checks who really sent a config message. Valheim lets a client write its own sender id on this kind of message, and the server did not check it. So a modified client could pass itself off as an online admin and change the server's synced settings, or pass itself off as the server and hand every player a different sea for a while. The server now drops any Undertow config message whose sender does not match the connection it arrived on, and logs one warning per connection that tries it. The admin check, the ranges and the synced keys are unchanged. The check runs on the server, so the server needs 1.0.2; clients need nothing new.

  • Less garbage per physics tick on a client connected to a server. Reading a synced value and noting the last pushed hull no longer create a new string every tick.

  • The DLL no longer carries the build machine's folder path. Every DLL through 1.0.1 embedded the absolute path of its debug-symbols file, a path that included the build machine's user name. The build now maps its source folders to a neutral /_/ prefix, so neither the DLL nor its symbols name a local folder, and two builds of the same commit, from Windows clones with Git's default line endings, the same .NET SDK and the same game libraries, are byte-identical. This DLL was built from the commit tagged v1.0.2; the GitHub release names that commit and gives the DLL's md5.

  • The README's Support section links the Raven Iron website, https://ravenirongames.com, beside Patreon and Discord.

  • The store page's website link now goes to the Raven Iron website. manifest.json's website_url pointed at the GitHub repo; it now reads https://ravenirongames.com/, matching the README's own Support section.

Tested in game on 2026-09-24, on a Valheim 1.0.15 dedicated server (crossplay) plus a single-player world, with one client, all on the DLL built from 5446c9d, the head of the fix branch (md5 8e35436a412c89cbfe7d45a7aa23b2d5, 132,608 bytes) — the same code as this release; only documents and the store page's website link changed after it. Built against and tested on Valheim 1.0.15. Flotsam in single player, with FlotsamPerHour raised from its default 6 to 120 for the test: 6 spawns in about 3.5 minutes, 34–107 m from where the drift began, in 30 m of water with a 0.30–0.33 m/s current. Config sync on join: the client asked the server for its config and nothing was dropped (the "13 values in force" line was read on screen, not in a log). Admin push: 9 of 9 MaxCurrentSpeed changes were accepted in an earlier session the same day, on the same DLL, and the last one set the value back. Local config was restored on leaving a server. Both sides booted with Harmony patched 4; Undertow logged no errors or warnings on either side, and no exceptions appeared in either log. Not tried in game: a non-admin's config push being refused (checked only in code); the forged sender itself, which needs a modified client (the packet-header read was checked against the 1.0.15 game code; six harness cases cover the accept/drop predicate but not the patch that calls it; genuine admin traffic passing through the patch is the closest in-game evidence); a 1.0.1 client with a 1.0.2 server, or the reverse (the handshake is unchanged, read from the code); flotsam staying off on a dedicated server with an empty ocean (an unchanged path; the harness covers the origin count); flotsam around a listen host's own player with a guest connected, which needs a second player (new in 1.0.2: the harness covers the origin count, and single player ran the host branch in game); and wake drift naming the last hull, then reading back (none) once that hull is destroyed, which is the check for the per-tick garbage fix; the garbage saving itself was not measured (it is read from the code). Off-game: 495 checks, 0 failed.

1.0.1
  • A hull under way now pays the current as a speed, not a force — 1.0.0 stopped a paddled karve in 0.2 m/s of water. Reported within hours of 1.0.0 going up (Grishak: below a MaxCurrentSpeed of 0.25 he could paddle through anything; at 0.3 the water held him or pushed him back). Read out of the game's own hull prefabs the same hour: a karve's paddle is 0.004 m/s of speed per physics tick, and the push this mod applied against a hull driving upstream was the water's speed × 0.02 per tick — the same 0.004 at 0.2 m/s of water. The push was an acceleration measured against the hull's engine, and vanilla's engines are tiny, so whether a current stopped a boat depended on the boat and not on the water. Every measurement before 1.0 had been a drifting hull, which the push models well, or a hull under Njord and Sailing, whose thrust is many times vanilla's.

    Now the ship's own speed setting decides. A hull with paddle or sail set feels the current as drag relative to the water — the exact correction to vanilla's own damping, which acts on absolute speed — so going into a current costs exactly the water's speed over the ground, going with it pays exactly that, and speed through the water is untouched. A hull adrift keeps the carrying push every drift measurement was taken on, so nothing about drifting changed. Watched the same afternoon on a paddled karve, verbose: 1.5–1.9 m/s of headway straight into 0.25–0.31 m/s of water, where 1.0.0 stalled; 3.0–3.5 m/s along a 0.44 m/s current running with it; mode adrift and the water taking the hull the moment the paddle stopped; zero exceptions. The harness paddles a karve and a raft tick by tick and pins both the fix and the 1.0.0 number as the defect it was.

  • UnderWayDragFactor, a new synced dial. How much of the water's own drag a hull under way pays: 1 is the physics above, 0 lets a boat under way ignore the current entirely (it is still carried when adrift), 2 grips twice as hard. DriftStrength now governs only how quickly a drifting hull is carried. Thirteen dials travel from the server instead of twelve; an older client drops the line it does not know, so the wire header did not move.

  • A boot line names every hull's real constants. Hulls (vanilla prefab constants; …): paddle force, sail factor, the three dampings, buoyancy, mass, and the Rigidbody's own drag (zero, on every hull) — because the class defaults a decompile shows are overridden by every prefab, and the diagnosis above needed numbers nobody had. INFO, once per boot, both roles.

8 older entries, full version history & downloads →

Threads

Log in to post
Manual installation instructions
1

Install BepInExPack Valheim

BepInExPack Valheim is required to run mods in Valheim.

Download BepInExPack Valheim 5.4.2351 · View mod page

Check out the mod page for detailed installation instructions.

2

Install Undertow

This mod must be installed on both the client and the server.

Download Undertow 1.0.3

Extract the ZIP and place the file(s) into the BepInEx/plugins/ folder inside your Valheim game folder.