You are viewing a potentially older version of this package. View all versions.
Sarkastic_gg-Dedicated_Simulation-1.26.0 icon

Dedicated Simulation

Server-side BepInEx plugin for Valheim dedicated servers: the server simulates the world itself. Character guard, item ledger, portal and ward guards, base decay, anti-cheat, fire control. Fork of Serverside Simulations by ddormer; clients stay vanilla.

Date uploaded 3 days ago
Version 1.26.0
Download link Sarkastic_gg-Dedicated_Simulation-1.26.0.zip
Downloads 33
Dependency string Sarkastic_gg-Dedicated_Simulation-1.26.0

This mod requires the following mods to function

denikson-BepInExPack_Valheim-5.4.2351 icon
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.2351

README

Sarkastic.gg Dedicated Simulation

version: 1.26.0 players: vanilla clients Valheim: 1.0.20 BepInEx: 5.4.2351 license: MIT (own code)

Source code · Report a bug · Changelog · Resource Regrowth

Join us on Discord Support us on Patreon

Fork of Serverside Simulations by ddormer, which is no longer maintained as of 2026, renamed at the original authors' request. Updated for Valheim 1.0, building on ddormer/valheim-serverside#118 by @mreastman.

The dedicated server simulates the world — monsters, physics, ships without a driver — instead of handing each area to whichever player got there first. Server-side only: players keep vanilla clients.

Companion plugin: Resource Regrowth (Thunderstore) brings back surtling cores, chest loot, creatures and whole zones in areas left alone; the two are made to run together.

Updated for Valheim 1.0.20 (the game's own code this mod depends on is unchanged from 1.0.16; the Ward guard and the keep-clear rule of the Portal lock, added in 1.19, were tested on 1.0.20, the older features on 1.0.16 and they have run on 1.0.20 since). Up to version 1.12 it was also checked on 1.0.15 and 1.0.12. A startup check warns about every vanilla method the mod uses that a game update has changed.

Why, compared to vanilla

In vanilla, the first player to enter an area owns it: their game runs the monster AI and physics there, and everyone else nearby sees that area through them. If that player has a poor connection or a slow PC, everyone around suffers — monsters jump around, hits land late — and updates travel from each player to the server, on to the owner and back.

With this mod the server owns and simulates those areas:

  • Each player depends only on their own connection to the server, not on someone else's.
  • Clients no longer run AI and physics for the areas they would have owned, which helps slower PCs.
  • Ships are handed to their driver, so steering has no round trip.

What it costs:

  • The server needs more CPU, RAM and upload than a vanilla server.
  • A player alone in an area now has their round trip to the server where vanilla would have had none. With a nearby server this is rarely noticeable.

Observed on one server

Valheim 1.0.7, Windows dedicated server, up to four players, September 2026. One group's session, not a benchmark.

  • No exceptions or mod warnings during play.
  • Items picked up from the ground: 98% of 437 on the first ownership request (1.2.0), 214 of 214 (1.5.0); the rest within 2 s.
  • The per-player send queue was full in at most 0.2% of send ticks, only in bursts such as portals.
  • About 0.8 of a CPU core on average with one player, and 15–25% of a core while empty (with the job worker cap). RAM about 1.6 GB empty, 2–2.6 GB with players, levelling off.

Not covered yet: Frost Foundry, sailing, raids, Deep North events, a non-default -simulationdistance, and the console commands under a Windows server panel.

What this fork adds

Compared to Serverside Simulations 1.1.9 (details in the changelog):

  • Valheim 1.0 support, and a review of every patched method against the 1.0 code.
  • Fixes: location prefabs were never released (a memory leak); zones could be generated before their locations; no objects were created with a non-classic -simulationdistance; 1.0 errors on the server with ship sails, the Frost Foundry (which duplicated items) and leviathans; the far ring of unexplored land was not pre-generated as in vanilla, so distant trees, cliffs and the Mistlands mist appeared late.
  • Objects nearest to a player are created first, e.g. after a portal.
  • Server-side networking limits from BetterNetworking, with a per-player log of how often they are reached.
  • Cap on Unity job worker threads, which otherwise idle at CPU cost on many-core hosts.
  • Fix: player changes that the save skipped. Valheim 1.0 rewrites only the world chunks it marked as changed, and a change received from a player marks nothing, so what a player just built or moved could be missing after a restart.
  • Fix: teleported players left behind as ghosts. A player who portalled or respawned stayed visible to the players near the old spot, frozen, until they next crossed a zone line, because Valheim 1.0 checks whether an object left their area before it stores the new position.
  • Fix: fireplaces smothered for everyone but the server. A smoke source only puffs while the local player is within 64 m, and a dedicated server has none, so the server never saw smoke: a fireplace in a room full of smoke, which every player sees go out, kept burning on the server -- using fuel, throwing sparks and setting what touches it alight -- and a fire under a roof never choked.
  • Admin commands on the server console: give <item> <amount> <player>, broadcast <text> (\n for a line break, \u010d for a letter by its code), event <name> <player>, players, characters, allow <name>, save, stop, typed into the panel the server runs in (AMP), and anything else goes to the game's own console (kick, ban, banned, stopevent, setkey, removekey, and after devcommands the cheats that need no player: skiptime, listkeys, resetkeys ...). A vanilla dedicated server never reads its console, and Valheim 1.0 does not let a player on a dedicated server use spawn from the game console, admin or not.
  • Smoother server frames: world updates reach every player at a steady interval however many are online, one slow frame no longer makes the next one slow through physics catch-up, and new zones are generated one per tick instead of one per exploring player. A periodic log shows frame times and what they are spent on.
  • save and stop console commands for server panels that write to standard input.
  • Character guard (off by default): a check on the characters players join with, server-side only, so clients stay vanilla. A character this world has not seen must be fresh, and a known one that comes back changed was played elsewhere -- see below.
  • Item ledger (off by default): an account per character of the items that lock progress, what it got here and what it put back into the world; items that came from another world are logged, or taken away -- see below.
  • Night spawn guard (off by default): the spawns that come with boss progress (Greydwarfs, Draugr, Skeletons, Goblins, Seekers, Charred, ...) held back around a player who has not got that far themselves, even where the world as a whole has moved past it -- see below.
  • Guardian stones (off by default): a boss's trophy on its guardian stone, and so its power, only for players who have beaten that boss themselves; to everyone else's game the stone is empty -- see below.
  • Fire control (off by default): a limit in metres on how far fire spreads from its source, a cap on spark generations, fireplaces that never set what touches them alight, or fire from projectiles only, so a player's own build never sets it on fire -- with the reach of every fire source in the game worked out, and every fire and fireplace simulated by the server so the rules hold -- see below.
  • Portal lock (off by default): a pair of portals used only by those it belongs to -- the builders, or the owner and the players on the ward over it; a name starting with * is open to everyone. Two portals are paired only if both builders may use the pair, so a portal built with the name of someone's unpaired one no longer walks anyone into their base; no public portal near the spawn, a boss altar or a trader; and with Protect, a portal a ward holds, and the far end of its pair, can be hit or taken down only by the ward's list -- see below.
  • Ward guard (off by default): the game's ward (guard stone) enforced by the server. In the game every check of a ward is made in the acting player's own game and nothing stops a weapon from damaging a warded build (the ward only flashes), nor a player from using a warded smelter, kiln, fire pit, bed or crop. The server judges what players' games send it, by the game's own rule: where an enabled ward covers the place, the ward's owner and the players on its list may act, nobody else -- hitting, taking down and repairing builds, opening chests and doors, taking the product out of smelters, switching fires off, claiming beds, picking crops, and more. A packet whose sender is not its connection is dropped, and so is a ward switched off or edited in another player's name; and no player may keep more than a few wards switched on (MaxActiveWards, 2) -- see below.
  • Base decay (off by default): what players built is taken down by the server once the players it belongs to have stayed away long enough, so abandoned bases do not fill the world for ever. How long depends on the best protection a piece has: a ward that is switched on (14 days away, then 10 days over which the base goes), a workbench within reach (7 + 10), or nothing (3 + 4). The ward goes first, then roofs before walls, chests and beds, the workbench last; a player who comes back stops what is left of it; players are told at login, three days ahead and while it runs. A dry run (log only) first -- see below.
  • Anti-cheat (off by default): the server looks at every object a player's game creates and takes away -- and by default kicks the player for -- what the unmodified game cannot make: a thistle, berry bush or ore planted with the cultivator, crops put down in groups faster than the game plants (unless the player's stamina paid for them: PaidPlanting), a piece placed in someone else's ward, far from the player or where nothing may be built, a stack larger than its item can be, the field overrides of the build-anything admin hammers, a character that flies. It can only see what a cheat leaves in the world; below is what 65 popular cheat and tweak mods leave -- see below.
  • Location icons (off by default): a trader's (Haldor, Hildir, the Bog Witch) and other locations' map icons only for players who have found that place themselves; vanilla shows them to everyone the moment anyone's presence has generated the zone -- see below.
  • Dvergr trade (off by default): the dvergr of the Mistlands are friends, and their camps hold what a player needs. Next to each dvergr ward the server puts up a chest and a sign; what a player puts into the chest and closes -- seeker trophies, say -- is weighed against a list of offers, the price taken and the goods (black marble, soft tissue, black cores ...) left in its place, or the camp's ward switched off for some hours and its angry dvergr calmed, so what lies in the camp can be taken without a fight. Vanilla clients; nothing to install -- see below.
  • Processor placement (off by default): the server pinned to one chiplet of an AMD EPYC or Ryzen (a die with its own L3 cache), the least busy one at startup, or to processors you name, and a higher process priority.
  • Safety: a startup check warns when a vanilla method the mod replaces has changed in a game update; if the core patches cannot be applied, the mod removes itself and the server runs vanilla.

Installation

  1. Install BepInExPack_Valheim 5.4.2350 or newer on the dedicated server.
  2. Copy SarkasticGG_Dedicated_Simulation.dll from the latest release into BepInEx/plugins/.
  3. Back up the world and restart the server. BepInEx/LogOutput.log should show Sarkastic.gg Dedicated Simulation installed and Vanilla drift check passed.

Clients need nothing.

Upgrading from Serverside Simulations: delete Serverside_Simulations.dll. Both use the same plugin GUID, so only one can load; the config file MVP.Valheim_Serverside_Simulations.cfg carries over.

Do not also run BetterNetworking on the server: its server-side limits are built in.

Configuration

BepInEx/config/MVP.Valheim_Serverside_Simulations.cfg, read at startup:

Setting Default
[General] Enabled true Turn the mod off without removing it.
[MaxObjectsPerFrame] Enabled true Turn the object creation limit off (vanilla's 100 per frame stays).
[MaxObjectsPerFrame] MaxObjects 100 Objects the server creates per frame, 1 to 10000. A vanilla dedicated server creates 100; higher loads areas faster at more CPU, lower fills them in more slowly.
[Networking] Enabled true Raise the limits on how fast the server sends world data to each player (the server side of BetterNetworking). Needs a restart.
[Networking] QueueSizeKB 48 Data queued per player before the server holds world updates for that tick (Valheim: 10). 48 KB at 20 ticks/s is about 960 KB/s, just under the send rate cap; above 80 Steam starts failing.
[Networking] SteamSendRateMinKB / MaxKB 256 / 1024 Steam send rate per player, KB/s (Valheim: 150). Keep min × players below the server's upload.
[Networking] StatsIntervalMinutes 5 How often to log, per player, how often the send queue was full. Near 0% means the limits are not what holds you back. 0 disables.
[Server] CpuAffinity (empty) Processors the server runs on. Empty: wherever the system puts it. auto: one chiplet -- on AMD EPYC and Ryzen a die with its own L3 cache (on the first EPYC generation also its own memory) -- the least busy over one second at startup, read from the system's own topology (Windows: processor dies, else NUMA nodes, else L3 groups; Linux: /sys), within the processors the process may already use. Or a list such as 16-31 or 0-7,32-39. Processors above 63 are not used. Keep UnityJobWorkers below the chiplet's processor count. Needs a restart.
[Server] ProcessPriority Normal Normal, AboveNormal or High: the server's priority against everything else on the host (Linux: nice -5 / -10, which needs CAP_SYS_NICE). Needs a restart.
[Server] MessageLineLength 50 Longest line, in characters, of a message in the middle of a player's screen (anti-cheat, wards, portals, the welcome, broadcasts); the top left corner, in letters half as big, takes twice as many. A vanilla client never wraps them, so a longer line ran off both sides of the screen. Broken at spaces; 0 = one line as before. Needs a restart.
[Server] MessageSecondsPerLine 3 How long a message in the middle of a player's screen is held, for each line of it (at most 30 seconds in all). The game fades such a message out over 4 seconds from the moment it arrives, so one of several lines was half gone before it was read; the server sends it again every second for this long, so it stays as bright as at first. Each repeat is one more entry in the player's message log (Compendium, Logs), which keeps the last 50. A kick that follows a message (anti-cheat, character guard) waits until it has been held. 0 = shown once, as the game does.
[Server] UnityJobWorkers 8 Upper limit on Unity job worker threads (Unity: one per CPU core). Only ever lowers the count; 0 leaves Unity's default.
[Server] ConsoleCommands true Read commands from standard input: save, stop (saves first), players, give <item> <amount> <player> (drops the items in front of that player; the name may be a unique beginning), broadcast <text> (a message in the middle of every player's screen; in the text \n is a line break, \u010d the letter with that code, \\ a backslash), event <name> <player> (a raid at that player; events lists them), help; anything else goes to the game's own console. In AMP this is its console, see the AMP chapter.
[Server] MaxGiveAmount 1000 Most items one give drops.
[Server] ConsoleInputCodePage 852 How a console line that is not valid UTF-8 is read (UTF-8 is always tried first): 852 Central European DOS, 1250 Central European, 1252 Western, 65001 UTF-8 only. AMP on Windows writes its OEM code page, 850 on a Western one, whose á é í ó ú ý (and capitals) have the same bytes in 852; the letters 850 lacks (č ď ě ň ř š ť ů ž) are plain ones by the time the line arrives, so in broadcast write them as \u010d. The log shows the bytes the first time it matters.
[Performance] SendIntervalMs 100 How often each player gets world updates. Valheim serves one player per frame, so with N players each waits N+1 frames (330 ms at 15 FPS with 4 players). Each send costs server CPU; see the performance log. 0 keeps Valheim's behaviour.
[Performance] MaxCatchUpMs 100 Longest frame counted in full. After a slow frame Unity reruns physics and every creature's fixed update for each 20 ms missed (Valheim allows 200 ms, 10 times); 100 caps it at 5. Game time runs slightly slow during such frames. 0 keeps the game's setting.
[Performance] MaxZonesPerTick 1 New zones generated per zone tick (10 per second), players taking turns. 0 = one per player per tick, as before.
[Performance] ServerTargetFps 60 Frame rate the server aims for (the game sets 30). With time to spare a frame no longer waits 33 ms, so reactions to players halve; under load it changes nothing. 0 keeps 30.
[Fixes] SaveClientChanges true Count a change that arrives from a player as a change to its world chunk, so the next save writes it. Valheim 1.0 rewrites only changed chunks and skips those.
[Fixes] TeleportGhosts true Tell the players near the old spot to drop a player who teleported away. Valheim 1.0 checks whether an object left their area before it stores the new position, so the teleported player stayed there for them, frozen, until they next crossed a zone line.
[Fixes] ServerSmoke true Let fires and fireplaces make smoke on the server while a player is within 64 m, as they do on that player's computer in vanilla: a fireplace in a smoke-filled room goes out on the server too, and fires under a roof can choke (see Fire control below).
[Fixes] DungeonLoadGuard true Keep a dungeon whose room bundle fails to load from wedging its zone. When Unity refuses a bundle as already loaded, the game still reports the load as done and then throws, so the dungeon never spawns and its zone stays flagged as loading. The guard uses the bundle Unity already holds, reports a load that really failed as failed, and lets such a dungeon go so it is tried again next time. A vanilla dedicated server never loads dungeons; this mod does.
[Compat] ValheimCommunityPatchUnload false Only with ValheimCommunityPatch installed. Its zone-diff unload drops objects outside the simulation distance of the server's reference position -- the world origin -- so objects around players are destroyed and created again on every pass. Off: that patch of it is removed when the world starts. On: it is kept, and the object lists are marked as edited on every pass so it takes the game's own unload check.
[Compat] ValheimCommunityPatchSpawnQueue false Only with ValheimCommunityPatch installed. Its spawn queue orders new objects by distance from the world origin, so this mod's nearest-player ordering never runs. Off: that patch of it is removed when the world starts. On: it is kept.
[CharacterGuard] Enabled false Check the characters players join with (see Character guard below).
[CharacterGuard] NewCharacters RequireFresh RequireFresh: a character this world has not seen must still be ready for Eikthyr's raid and wear nothing beyond a level 1 workbench. Allow: every new character is let in and remembered.
[CharacterGuard] NewCharacterAction Kick For a new character that is not fresh: Kick (after a message on their screen), Log or Ignore.
[CharacterGuard] ChangedAway Log For a known character that comes back wearing something new or with other progress: Log, Kick or Ignore.
[CharacterGuard] ExemptAdmins true Admins are never checked.
[CharacterGuard] FreshEvent army_eikthyr The raid a fresh character is still ready for. The game stops that once a character knows the antler, the bronze or iron pickaxe, hard antler or Eikthyr's trophy.
[CharacterGuard] NewCharacterMessage / ChangedAwayMessage (English text) Shown in the middle of the player's screen before the kick.
[CharacterGuard] Welcome false Greet a character the first time it joins this world.
[CharacterGuard] WelcomeTitle / WelcomeLines (English text) The greeting in the middle of the screen, then the lines (separated by |) in the top left corner.
[ItemLedger] Mode Off Off, LogOnly or On: keep item accounts per character (see Item ledger below); On also takes away items that came from another world.
[ItemLedger] Items auto auto: the items that lock progress, from the game's own data (see below). Or item prefab names, comma separated. Items traders sell are always left out.
[ItemLedger] ExtraItems / ExcludeItems (empty) Item prefab names to add to the list, or never to keep accounts of.
[ItemLedger] LogAllMovements false Also log every movement of a tracked item (and every craft the ledger counts), not only what cannot be accounted for.
[ItemLedger] ExemptAdmins true Admins get no account and are never checked.
[ItemLedger] GraceHours 168 For this long after the ledger first runs on a world, no character counts as new: the players who are already around come back with what they had.
[ItemLedger] Message / AltarMessage (English text) Shown in the middle of the player's screen when items are taken away, or a boss altar refuses them.
[NightSpawnGuard] Enabled false Hold back the spawns that come with boss progress until every player in the spawn zone has got that far themselves (see Night spawn guard below).
[GuardianStones] Enabled false Show a boss's trophy on its guardian stone, and so offer its power, only to players who have beaten that boss themselves (see Guardian stones below).
[GuardianStones] ExemptAdmins true Admins see every stone as it is.
[GuardianStones] Message (English text) Shown to a player standing at a stone that holds a trophy they may not use, at most once a quarter of an hour per stone; {0} is the boss. Empty: no message.
[PortalLock] Enabled false Let a pair of portals be used only by those it belongs to (see Portal lock below).
[PortalLock] ExemptAdmins true Admins may use and rename every portal.
[PortalLock] PublicPrefix * A portal whose name starts with this is open to everyone. Empty: none.
[PortalLock] ExtraReach 0 Metres added to a ward's own radius (32 m for the game's ward) when deciding whether it covers a portal.
[PortalLock] Message / PairingMessage (English text) Shown at a portal a player may not use, and to the builder of one that cannot be paired, at most once a quarter of an hour per portal.
[PortalLock] KeepClear StartTemple,Eikthyrnir,GDKing,Bonemass,Dragonqueen,GoblinKing,Mistlands_DvergrBossEntrance1,FaderLocation,DN_Bossroom,Vendor_BlackForest,Hildir_camp,BogWitch_Camp Locations near which a player may not have a public portal: the spawn, the boss altars, the traders (every place the world has for each, a trader's other possible places included). Empty: none.
[PortalLock] KeepClearRadius 100 How far, in metres sideways, from any of them a public portal may not stand. A player-built portal there does not count as public and cannot be given a public name. 0: the rule is off.
[PortalLock] KeepClearMessage (English text) Shown to a player whose portal is refused a public name there; {0} is the radius.
[PortalLock] Protect false Who may hit a portal or take it down with the hammer: where a ward holds it, the ward's owner and list; where none does but one holds the other end of its pair, that ward's list; where no ward holds either end, anyone. Admins always.
[PortalLock] ProtectMessage (English text) Shown to a player whose hit or removal of a protected portal is refused, at most once a quarter of an hour per portal.
[WardGuard] Enabled false Make the server enforce the ward (see Ward guard below). Needs a restart to switch on; switching it off takes effect at once.
[WardGuard] Structures On Hitting, taking down with the hammer and repairing a player's build (and the ward itself) inside an enabled ward by a player who is not on its list; a hit with no attacker or by a creature the sender's game owns is not a player's. On: dropped, the ward flashes and the player is told. LogOnly: only written to the guard log. Off: nothing.
[WardGuard] Interactions On The same for opening chests and doors, stands, fermenters, beehives, sap collectors, incinerators, the map table, and what the game does not guard: taking the product out of a smelter, kiln or cooking station, switching a fireplace off, claiming a bed, picking planted crops. Putting something in, and traps, are left alone.
[WardGuard] Spoofing On Drop an RPC whose sender id is not its connection, and a ward's ToggleEnabled / TogglePermitted for a player id other than the sender's own. Admins are not exempt.
[WardGuard] DestroyRequests LogOnly A request to destroy an object that another game or the server owns, which the game's own client never sends: LogOnly counts and logs them, On takes them out of the request. Leave it in LogOnly until the log shows an honest client sends none.
[WardGuard] ExemptAdmins true Admins are not held to any ward.
[WardGuard] Flash true The ward flashes (its own shield effect) when it refuses something, at most every 3 seconds.
[WardGuard] Message (English text) Shown to a player the ward refused, at most every 15 seconds. Empty: no message.
[WardGuard] MaxActiveWards 2 How many wards one player may have switched on at once; a player who has that many and tries to switch on another is refused and told (switching one off or taking it down makes room). Wards already on stay on if the limit is lowered. Admins are not held to it. 0: no limit.
[WardGuard] MaxActiveWardsMessage (English text) Shown to the player so refused; {0} is the limit.
[Decay] Mode Off Off, DryRun (tracks players and writes to the log what would be taken down, changes nothing) or On: take down what players built once the players it belongs to have stayed away (see Base decay below).
[Decay] WardGraceDays / WardFadeDays 14 / 10 A base covered by a ward that is switched on starts to decay this many days after the last of its owner and the players on its list was active; its pieces then go over the fade.
[Decay] WorkbenchGraceDays / WorkbenchFadeDays 7 / 10 The same for what has no ward but stands within WorkbenchRadius of a workbench; kept by the workbench's builder.
[Decay] WorkbenchRadius 20 How far, in metres sideways, a workbench protects the pieces around it.
[Decay] NoneGraceDays / NoneFadeDays 3 / 4 The same for a piece with neither; kept by its builder.
[Decay] ActiveMinutes 10 A player counts as active when they have been online this long in one go.
[Decay] ScanMinutes 10 How often the world is looked through (some tens of milliseconds) to start decay, take down what is due and update what players are told.
[Decay] MaxRemovals 200 Most pieces taken down in one pass.
[Decay] ExemptAdmins false Admins' characters count as always active (the server learns that a character is an admin when it is online). Off by default: nobody is exempt unless named with decay exempt.
[Decay] LoginMessage true Tell a player half a minute after they join about their bases that are decaying or will start to in three days.
[AntiCheat] Enabled false Look at what players' games create and take away or kick for what the game cannot make (see Anti-cheat below). Needs a restart to switch on.
[AntiCheat] Action Kick What happens when a rule that is On is broken: Log (write it down), Revert (also take the object away), Kick (also tell the player in the middle of the screen and remove them six seconds later; they can come back and what breaks a rule is taken away again).
[AntiCheat] ExemptAdmins true Admins are not held to the rules.
[AntiCheat] Message (English text) Shown to a player about to be kicked; {0} is what they broke.
[AntiCheat] SoftStrikes / WarnMessage 2 / (English text) Distance, Wards, NoBuild, Biome, Dungeon, Cultivated and Identity depend on the state of the server a moment after the player's game decided: the first time one is broken in ten minutes the object is taken away and the player gets WarnMessage, the SoftStrikes-th time (15 s or more later) they are kicked. 1: kicked at once.
[AntiCheat] UnknownPrefabs, NotBuildable, Forged, Identity, Creator, Distance, Rate, Items, CheatMarks, Fly, Fields, Flood, Level, Wards, NoBuild, Biome, Dungeon On One rule each (see below): Off, LogOnly (only written to <world>.guard.log) or On (does what Action says).
[AntiCheat] CheatMarksOther, Cultivated, Burst, Health, Rotation, Teleport, Speed LogOnly The rules that can be wrong for an honest player or that break nothing but the look: a trace in the log, nothing more, until set to On.
[AntiCheat] MaxLevel 10 The highest creature level let through (the game makes up to three stars, level 4).
[AntiCheat] MaxQuality 0 The highest quality of a dropped item let through; 0 = not checked. The upgrade station of 1.0 raises quality past the recipe's top with no ceiling of its own, so leave this at 0 unless you know a number your players cannot reach.
[AntiCheat] MaxDistance 30 Metres from the player at which a piece is "far" (the game places within 5 m of the eye, a few more for large pieces). 0: no limit.
[AntiCheat] MinInterval / RatePlants 0.25 / 3 RatePlants or more plants of one player within MinInterval seconds are more than the game plants (one in 0.4 s).
[AntiCheat] PaidPlanting / PlantStamina false / 1.3 true: a group of plants that Rate would take is let through when the cultivator was in the player's hand and the stamina of their character went down by PlantStamina for each plant of it (see "Paid planting" below); the plants it did not pay for are taken away and Rate acts as before. The game takes 1.625 at the least for a plant, times the world's stamina rate; a mod that charges less than PlantStamina does not pay. Needs a restart.
[AntiCheat] BurstPieces 15 Pieces (not plants) from one player in three seconds that make a burst.
[AntiCheat] TeleportMetres / SpeedLimit 250 / 30 A move of this many metres between two looks (every 2 s) with no portal at either end; metres per second sideways in two looks in a row.
[LocationIcons] Enabled false Show a location's map icon (Haldor, Hildir, the Bog Witch, Hildir's dungeons, the Deep North boss room, the ancient upgrade station) only to players who have been near it themselves; remembered per character in <world>.icons.txt. The start temple's icon stays for everyone.
[LocationIcons] DiscoverRadius 0 How near (metres, horizontally) a player must have been to a location for its icon to appear on their map. 0: as near as the game itself shows it -- the location's zone within the simulation distance of the player's zone (4 zones by default, about 256-320 m straight ahead).
[FireControl] Enabled false Limit how far fire spreads (see Fire control below). With the three settings below at their defaults it changes nothing.
[FireControl] MaxRadius -1 A fire starts only within this many metres (straight line) of the source its chain began at. -1: no limit.
[FireControl] MaxSpread -1 Caps how many further generations of sparks any fire may set off. 0: nothing throws sparks except an arrow or fireball where it lands. -1: as in the game.
[FireControl] FireplaceIgnition true Lit fireplaces set alight whatever burnable touches their flame. Off: a hearth, brazier or torch never starts a fire; a bonfire or campfire still throws sparks.
[FireControl] OnlyProjectiles false Only projectiles start fires -- a fire arrow, a Staff of Embers fireball, a meteor, a lava rock -- and what they set alight burns and spreads as before (within MaxRadius and MaxSpread). Nothing a base has (fireplaces, the fires they lit, the troll a player summons) throws a spark or sets anything alight. Surtlings' and fuling shamans' fireballs never set anything alight in the game.
[DvergrTrade] Enabled false Put up a trade chest and sign at the dvergr wards (see Dvergr trade below). Needs a restart to switch on; switching it off puts up nothing new and takes nothing down.
[DvergrTrade] Offers TrophySeeker:3 > BlackMarble:20, TrophyTick:2 > Softtissue:10, TrophyGjall:1 > BlackCore:2, TrophyHare:1 > Lantern:1, TrophySeekerBrute:1 > Ward:24 Comma separated price > reward. The price is one or more Prefab:count joined by +; the reward is Prefab:count or Ward:<hours> (decimals allowed). Paid in this order, each as many times as it is paid in full. Two offers that take the same item: the first wins; an unknown item skips its offer; both are warned about in the log. Read again when the value changes.
[DvergrTrade] OffersAshlands TrophyCharredMelee:3 > Grausten:20, TrophyAsksvin:2 > FlametalOreNew:5, TrophyMorgen:1 > MoltenCore:2, TrophyVolture:1 > Lantern:1, TrophyFallenValkyrie:1 > Ward:24 The same for the Ashlands' charred tower: the goods and trophies of its own land. Offers holds for the camps of the Mistlands. Empty, or not one offer in it valid: Offers holds for the Ashlands too.
[DvergrTrade] StandRange 48 Metres, sideways: a player this close to a dvergr ward makes the server put up that camp's stand, and is told when its ward goes off and on.
[DvergrTrade] SignTitle / SignTextSize (English text) / 45 The first line of the sign (one line per offer follows), and the percentage of <size=..%> around the text, which is also the size of the text a player reads when pointing at the sign (e.g. 70 makes that larger). The game writes a sign in dark brown, which hardly shows on the dvergr's dark board: <color=#F2E6C8> at the head of the title makes the whole sign light. How the game sizes the text: docs/DVERGRTRADE.md.
[DvergrTrade] SignLanguage (empty) The language of the item names on the sign, as the game's own language list names it (Czech, German, Polish ...): the game's own words, as the players' inventories show them. Empty: the server's language, English on a dedicated server.
[DvergrTrade] WardOffText / WardStateOn / WardStateOff ward off for {hours} h / Ward: on / Ward: off for {hours} h more The words that are not item names: how a ward offer reads on the sign and in {offers}, and {ward} in MessageOpen while the ward is on and off.
[DvergrTrade] MessageOpen (English text) Top left, to whoever opens the chest. {offers} is one line per offer (item names in the player's own language), {ward} the state of the ward.
[DvergrTrade] MessageTrade / MessageMissing / MessageFull (English text) In the middle of the screen, to the player who closed the chest: what was paid and left ({paid}, {got}), what an offer still lacks ({missing}), that the goods did not fit ({got}).
[DvergrTrade] MessageWardOff / MessageWardOn (English text) In the middle of the screen, to every player within StandRange of the camp when its ward is paid off ({hours}: the hours it is off for) and when it comes back on.
[DvergrTrade] MessageWardMax (English text) In the middle of the screen, to the player who closed the chest, when an offer of the ward was paid but its hours would take the ward past seven days from now (that offer is not taken, its trophies stay): {hours} the hours it is off for, {max} the most.
[Performance] StatsIntervalMinutes 5 How often to log FPS, frame times, physics steps per frame, the cost of world updates and zone generation, and what the slowest frame was doing, while players are online. 0 disables.

Character guard

Valheim keeps a character on the player's own computer and a vanilla client never sends its inventory to the server, so no server-side mod can read or replace what a character carries. The server does see a character's id (the same on every world), what it wears and holds (both hands, back slots, armour, utility, trinket, with the upgrade level of weapons), and the raids it is ready for, which the game works out from the items the character knows and the bosses it has beaten. The guard uses that:

  • New characters -- not in this world's list and without anything built, a bed or a tombstone here -- must be fresh: still ready for Eikthyr's raid (the game stops that once a character knows the antler, bronze or iron pickaxe, hard antler or Eikthyr's trophy) and wearing nothing beyond what a level 1 workbench makes. A character brought in with progress from another world gets a message and is kicked 8 seconds later (NewCharacterAction).
  • Known characters that come back wearing something past a level 1 workbench they did not have when last seen here, or ready for other raids than then, were played somewhere else in between; that is logged (ChangedAway, or kicked). A club or rags are made anywhere in minutes and tell nothing. What an online character wears and knows is noted every minute, at every world save and at shutdown, as well as when it leaves, since a server shutdown does not disconnect the players one by one.
  • Welcome (Welcome, off by default): a character let in for the first time gets WelcomeTitle in the middle of the screen and then each of WelcomeLines in the top left corner, a few seconds after joining. A kicked character gets its kick message instead.
  • Characters that built something or own a bed or a tombstone in the world count as known, so switching this on does not lock out existing players. Admins are exempt.
  • characters on the console shows how the guard sees who is online; allow <name> lets a character in (and one that is waiting for its kick stay). The list is <world>.characters.txt next to the world save.

What a character carries without wearing it stays invisible to the server: this stops characters being imported with their progress; the item ledger below catches materials brought in by known characters once they reach the world.

Item ledger

The server never sees a bag, but nearly every way into and out of one passes through an object it does see:

  • In: picking an item up (the player takes it over and deletes it; when the bag has room for only part of a pile, the part that went in is counted from the smaller stack the player writes back), taking it out of a chest, cart, ship or tombstone, or off an item stand or armour stand.
  • Out: dropping it, putting it into a container or on a stand, feeding it to a smelter, kiln, refinery, cooking station or fermenter, offering it at a boss altar, building with it, dying with it (the tombstone is the whole bag). A drop counts only if the item has been in a bag: the game marks everything in a bag, and what falls out of a destroyed piece, a creature or a rock carries no mark.
  • Worn: what a character wears and holds is checked every few seconds; an upgrade shows as a higher quality.

Crafting, upgrading, eating, buying from a trader and what a caught fish brings along are not seen. So a tracked item the character never got here counts as crafted if its recipe can be paid from the tracked items it did get, and the ledger reckons in the player's favour wherever the game leaves room: a craft at a station yields the most the crafting bonus can give (three more per craft for a lucky batch of five), an upgrade may have been made at Valheim 1.0's upgrade station, which takes only upgrader items, an item it held may have broken there and handed part of its materials back, and a caught fish counts in the most its extra drops can bring (a Fish10 one silver). What traders sell is not tracked.

Which items: Items = auto takes the items that lock progress, from the game's own data: what cannot go through a portal (ores, metals, dragon eggs), what bosses drop and what summons them, every material that all recipes and pieces using it need more than a level 1 workbench for, and every item that all its recipes need more for -- what a forge, cauldron, black forge or galdr table makes, and what goes into it. In Valheim 1.0.16 that is about 530 items; the guard log lists them by reason at startup.

When a character puts more of an item into the world than it ever got here, and could have made, the rest came from somewhere else. That is written to <world>.guard.log next to the world save; with Mode = On it is also taken away -- a dropped stack is cut down, a container or stand loses it once nobody uses it, a smelter drops it from its queue, a boss altar refuses to summon -- and the player sees a message. What was built with it, put on a cooking station or fermenter, or is worn is only logged.

  • A new, fresh character (see Character guard) starts at zero, so its account is exact from the start.
  • For any other character, what it carried when counting began is unknown. Its findings are logged as unverified and never acted on, until its first death here: the tombstone shows the whole bag, and from then on its account is exact.
  • For the first GraceHours after the ledger first runs on a world, no character counts as new.
  • The accounts are kept in <world>.items.txt.
  • A fault in the ledger or the guard is logged (at most 20 times in a quarter of an hour) and never stops the game's own code. If the accounts or the known characters cannot be read, that feature stays off until the next start and leaves the file as it is.

Start with LogOnly for a while and read the guard log before switching to On.

Night spawn guard

Some of Valheim's ambient spawns come with boss progress: Greydwarfs at night in the Meadows once Eikthyr is dead; Draugr, Greydwarf Elites and Shamans, and Odin's night visits, once the Elder is; Skeletons once Bonemass is; Goblins (the hostile wandering kind) once Yagluth is; Seekers, Seeker Brood and Ticks once the Queen is; Charred once Fader is. Vanilla gates each one by a single world-wide key, set the moment any player beats that boss anywhere: a new player can meet Seekers in the Meadows because someone else on the server has been to the Mistlands.

The guard holds such a spawn back in a zone -- the 64x64 m area vanilla spawns around; a spawn is placed 40-80 m from one of the players in it -- until every player in that zone has got that far themselves. Until then the spawn behaves there exactly as in a world where the boss still lives. In a group the least progressed player decides. Raids are not affected: with -setkey playerevents the game already picks raid targets by each player's own progress.

How far a player has got is read from the one thing a vanilla client tells the server about its progress: the raids it is still "ready for", which the client works out from the items it knows and its own keys (possibleEvents, the same list the character guard reads). The main raids form a chain -- each needs the previous boss's drop known and stops once the next boss's is -- so the first one still listed tells how far the player is. The forest troll and surtling raids, which need the Elder's and Bonemass's drop actually known, confirm those two steps, because the chain raids also stop for a player who merely took that boss's power at a stone, where someone else may have hung the trophy.

Checked against a port of the game's own raid check over its raid data (1.0.16): exact for every progress level, and no combination of other people's powers lifts a player at or below the Elder. Above that, a player looks further than they are only by taking the Queen's power and every power before it in the chain (from Bonemass's level Moder's too, from Moder's Yagluth's). A player counts a boss as beaten once they have picked up one of its drops: for Eikthyr the trophy or a Hard Antler, for the Elder the trophy or the Swamp Key, for Bonemass the trophy or the Wishbone (everyone at those two kills gets their own key and wishbone), for Moder the trophy or a Dragon Tear -- but for Yagluth only a Torn Spirit (or a Wisp or Demister from the Mistlands) and for the Queen only a Majestic Carapace, not their trophies. Fader leaves nothing of his own in the list, so his Charred count as the Queen's. A player whose list has not arrived yet counts as having beaten nothing. The Deep North's Jotun patrols are left as vanilla.

Guardian stones

A boss's power is taken at its guardian stone, wholly in the player's own game: the stone offers it when its stand holds the trophy, and the power is kept in the character, which the server never sees. So no server can take a power away again; what it can do is decide what each player's game knows about the stone. With this on, a player who has not beaten that boss themselves -- read from their raid list the same way as the night spawn guard, with the same drops counting -- is sent the stone without its trophy: to them it is an empty stone that offers nothing. Everyone else sees it as it is, and once a player gets that far their game gets the trophy too, within a few seconds.

  • Only players who may use a stone can hang its trophy. The others' game would offer to hang one from their inventory on what looks like an empty stand, which first asks the stone's owner for the stone. The server refuses that request, whether it owns the stone itself or a player's game does (it does not pass it on to that game, which would grant it), and a stone a player's game took over goes back to the server after 10 seconds.
  • The trophies stay where they are. The server never changes the stone; each player's copy is made as it is sent. Should a game ever send back a stone it was shown empty, the server keeps its own, takes the stone back and logs it. The game's guardian stones set no world key and their trophies cannot be taken down (both read from its data), so a stone shown empty changes nothing else.
  • A player's game gets the stones only once their raid list has arrived, so a veteran logging in at the temple never sees them empty first.
  • When someone hangs the very first trophy on a stone, players who may not use it see it for up to 4 seconds before their copy shows the stone empty again (the stand's own broadcast of the new trophy). Trophies cannot be taken down, so this happens once per stone.
  • A player standing at a stone they may not use is told why (Message), at most once a quarter of an hour per stone.
  • Fader leaves nothing of his own in the raid list; his stone counts as the Queen's.
  • Admins see every stone as it is (ExemptAdmins). A power a character took before this was switched on stays with it.

Portal lock

Valheim pairs portals by their name alone: any two unconnected portals with the same name are connected (Game.ConnectPortals, on the server, every five seconds), and a player who steps into a connected portal is teleported by their own game, which the server cannot stop. So anyone who builds a portal with the name of someone's unpaired one walks into that base -- an untagged portal pairs with any other untagged one -- and anyone who finds a paired portal can use it. What the server does decide is what each player's game is told about a portal, and the lock uses that.

A pair of portals is one thing, and the same player may use both ends (one let through one end must be able to come back through the other):

  • Where a ward holds an end -- the ward (PrivateArea) covers it in its own radius (32 m for the game's ward, the circle seen around it; ExtraReach widens it) and has the portal's builder on its list -- the owner and the players on that list may use the pair. One ward at one end decides for the pair, the far end included, wherever it stands; with a ward at both ends a player has to be on both lists. The ward's own list is used (its owner presses E on it, as before) and it counts whether the ward is switched on or off, so adding a friend never opens the portals. A ward put up over someone's portal later, which does not have its builder on its list, holds nothing.
  • Where no ward holds either end, the builders of the two ends.
  • Everyone, if the name starts with PublicPrefix (*) at both ends; inside someone else's ward only if that ward's owner built the portal. A name can still only be changed by those who may use the pair otherwise.
  • Admins (ExemptAdmins). A portal nobody has built (spawned by an admin, no ward) stays open.

How it is kept:

  • A player who may not use a pair is sent the portal's data with the connection set to "none" (ZDOMan.SendZDOs), and put back right after: to their game the portal is unconnected and does not teleport. Nothing is saved or changed on the server. A client only takes data newer than its copy, so when who may use a pair changes -- a name on a ward, a portal built, a ward moved -- the portal's data revision is raised once and the right copy goes out to everyone within a couple of seconds.
  • Pairing: two unconnected portals of one name are connected only if both builders may use the pair -- with a ward holding an end, each builder is on its list; with none, the same player built both. Two names alike are no consent. Otherwise they stay unconnected and the builder, standing at the portal, is told why. Pairs that exist are not touched, and where no ward holds either end they stay usable by the builders of both; where a ward holds an end, its list decides who may use the pair from then on, so a builder who was never written on it asks the ward's owner (the player is told at the portal).
  • A portal's name can be changed only by those who may use the pair (not by everyone just because it is public). The request is not passed on to the game that owns the portal, data from anyone else is not taken, and a player who may not use a portal is never given it.
  • Everything the game's own wards already do stays: a portal can be taken apart by whoever may take apart pieces in that ward, and nobody else in a ward's area.

Who may hit a portal or take it down (Protect, off by default). In the game a ward keeps only the hammer off what stands in it -- each player's game deciding for itself -- and nothing guards a portal out in the open, so anyone can smash the far end of a base's pair and cut the base off, and the warded end with a weapon. With Protect: where a ward holds the portal, its owner and the players on its list (an end with a ward of its own keeps its own list); where no ward holds it but one holds the other end of its pair, that ward's list, as if the ward stood over it; where no ward holds either end, anyone, as in the game; admins always. A player's game sends a hit or a hammer's removal to the game that owns the portal, never the game of a player who may not manage the pair, so the request passes the server: from anyone else it is not passed on (not handled, where the server owns the portal), the player is told (ProtectMessage) and <world>.guard.log gets a line. A hit from a player's game counts whatever made it -- a weapon, a catapult or a battering ram (their hits name no attacker), fire; a hit by a creature that game runs (a raid) goes through, and so do repairs. So a portal planted in someone's open ground and paired with a warded one can be taken down only by that ward's list or an admin. Not covered: the ward itself (a weapon breaks it while Ward guard is off, and then the portals are open), the floor a portal stands on, and a modified game that names another player as the sender (Ward guard's Spoofing drops that).

Where a public portal may not stand. A public portal next to the spawn, a boss altar or a trader makes a hub of the place that everyone passes. KeepClear names the locations (by default the start temple, the boss altars, Haldor, Hildir and the Bog Witch -- every place the world has for each, so a trader's other possible places count before he is found) and KeepClearRadius (100 m) how far, sideways, from any of them a public portal may not stand: a portal a player built there does not count as public (its builder and the ward's list use it, whatever its name starts with) and its name cannot be changed to a public one (the request is refused, the builder told why). A portal nobody built (spawned by an admin) is not held to it, so a hub at the spawn is the admin's to make. Portals that were public there when this was switched on stop being public; the log names each. Tested with two simulated players on a copy of the live world: pairs within 30, 99, 40 and 60 m of the spawn, a boss altar and Haldor were not usable by a stranger, a pair 150 m from the spawn and an admin-spawned pair 80 m from it were; a private name next to the spawn was untouched; a stranger's request to name a portal 30 m from the spawn '*...' was refused with the message and '...' went through.

What it does not do: it cannot stop a game that has been modified, and it does not hide where a portal stands. A player's character is what counts, not the account: a second character is another player.

Ward guard

What the game's ward (guard stone, PrivateArea) protects, and where that is decided (Valheim 1.0.20, from the game's own code and prefab data). Every check is made in the acting player's own game -- PrivateArea.CheckAccess reads the local player -- so the server believes whatever a game tells it: building, taking a piece down with the hammer, opening a chest or a door, changing a sign or a portal's name. Nothing checks the ward when a piece is hit: a weapon, an arrow or an explosion goes through WearNTear.RPC_Damage, which only makes the ward flash. A player with a sword can take down a warded wall, a chest or the ward itself (1000 health; an axe and a pickaxe do nothing to it, a sword, a mace or a club do), and the PvP switch is no help, it concerns players and not what they build. Smelters, kilns, cooking stations, fireplaces, beds and planted crops have no ward check at all. A ward also protects nothing until its owner has switched it on (E), and a player whose name is on the list of one covering ward may act in all of them.

With [WardGuard] Enabled the server decides, for every game that talks to it, by the game's own rule (PrivateArea.CheckAccess): a player may act where no enabled ward covers the place (the ward's radius, 32 m, measured sideways), or where one of the covering wards has them on its list -- its owner, or a player who put their name on it with E. Every routed RPC a client sends for an object that is not its own passes ZRoutedRpc.RPC_RoutedRPC on the server; it is looked at there, before anything is done with it, and judged by the player the connection belongs to. This mod's core patch makes the server the owner of what stands near players (vanilla gives it to a player's game), so that holds for the builds of others; only an object a player's own game owns -- one it has just placed, while it stays in the area -- handles its requests in that game without the server, and such an object is the player's own.

  • Structures (Structures): RPC_Damage, RPC_Remove and RPC_Repair for a build piece (anything with a creator, and the ward itself) in a ward, from a player who may not act there, are dropped; the ward flashes for everyone near, as it does for a hit, and the player is told. A hit with no attacker (fire, a falling object) or by a creature that the sender's game owns (a raid, a lured monster) is not a player's and is not touched: monsters do what they do in the game. Admins are not held to a ward (ExemptAdmins; for structures and interactions only, not for spoofing and destroy requests). Ships and carts are pieces: hitting one inside a ward is judged, steering it is not.

  • Interactions (Interactions): what the game's client already refuses (chests and doors with the guard stone flag -- the iron, wood, black metal and grausten chests, barrels and wardrobes, 20 kinds of door --, item and armour stands, fermenters, beehives, sap collectors, incinerators, writing on the map table) is refused by the server too, and so is what has no check: taking the product out of a smelter, kiln or cooking station, switching a fireplace off, claiming a bed, picking planted crops. A chest asked to open answers the player no, as it does when it is in use. Personal chests (piece_chest_private) are the creator's and need no ward. Putting something in -- ore, fuel, food, ammo -- is left alone: it is a gift, and the player's game has already taken the item out of their inventory when it asks, so a refusal would only destroy it. Traps are left alone too: the same request is sent when something steps on one.

  • Spoofing (Spoofing): the sender id in a routed packet is the client's own claim. A packet whose id is not the connection's is dropped; the game's own client always sends its own. A ward's ToggleEnabled and TogglePermitted take the player's id as a parameter: a modified game could switch off someone's ward by sending its owner's id, or put itself on the list -- both are dropped when the id is not the sender's own.

  • Destroy requests (DestroyRequests): the game asks the server to destroy objects its own game owns, so a request for objects that another game or the server owns is what a modified game sends. But the game also claims an object and destroys it in the same frame (the hammer on a piece that is not a WearNTear, picking up, harvesting), and the claim can reach the server after the request, so an honest client shows up in the count too. LogOnly (the default) counts them and writes a summary (five minutes after the first, or when the player leaves); On takes them out of the request. Leave it in LogOnly until the log shows what the honest clients do.

  • Limit on switched-on wards (MaxActiveWards, 2; 0: none): a ward is switched on by its creator pressing E on it (ToggleEnabled; the game does nothing for anyone else). A creator who already has that many wards on is refused -- the request is dropped and they are told in the middle of the screen -- until they switch one off or take it down. A ward that a player has just placed is owned by that player's game, which handles the key press itself without asking the server, so the limit is also kept on what is: every five seconds the server counts the wards that are on and switches off the newest of a player who has too many (taking the ward over and writing enabled off), and tells them. Building more is not held back. The wards that are on when the server starts, or when the limit is changed, stay on. A player is held to it while they are in the game, and every ward above the limit goes off, the newest first. Admins are not held to it. Without a limit the world fills with wards, and under Base decay every ward is a base that is kept alive; the limit asks no second kind of ward and no mod on the client.

Every refusal is written to <world>.guard.log (the character guard's and the item ledger's file): who, what, where, whose ward, at most once a minute per player, kind and ward, with how many there were since the last line. With LogOnly the line ends "LogOnly: let through" and nothing is stopped; set Structures, Interactions and Spoofing to LogOnly first to see what the rules would do (the limit on switched-on wards has no LogOnly: it is on at 2 as soon as the guard is). Enabled needs a restart to switch on; switching it off takes effect at once.

What it does not do: ground items, tombstones (anyone may open one, as in the game), steering a ship or pulling a cart, the text of a sign (written straight into its data by the player's game), terrain, and fire and falling or colliding objects (they have no attacker; Fire control limits fire). It cannot stop a game that takes ownership of an object without asking its owner (a boss trophy used on a turret does) and then hits it itself, nor a piece that is out of every ward's radius from carrying warded ones (taking down the pillar takes down what stands on it). And it cannot tell a modified game from an honest one, only refuse what an honest one never sends: a modified game can write an object's data directly, name another player's character as its own (the server takes the identity from the client's character data) or name a creature as the attacker of a hit.

Tested on a copy of the live world with three simulated players (a ward's owner, a stranger and a friend put on the ward's list) sending each request as a client does, with the guard off, On, LogOnly and with DestroyRequests On, and with a real, unmodified Valheim 1.0.20 client hitting a warded wall: off the list its hits were refused ("tried to hit woodwall ... refused") and the wall stayed untouched, on the list the wall was hit as in the game, and with the guard off it came down; the owner, a listed friend, creatures of the sender's game and admins were never refused. Finding the wards among 349 000 objects takes 6.5 ms every 5 seconds. The full run is in docs/TESTS.md.

Base decay

A world where every visitor leaves a camp behind fills up: the objects stay in the save, in memory and in every player's game for ever, and an abandoned base is a ward nobody can use and chests nobody opens. With [Decay] Mode the server takes down what players built once the players it belongs to have stayed away long enough. It is server-side only, so clients stay vanilla, and it is meant to be run as a dry run for two weeks first (DryRun: it tracks the players and writes what it would do to the log -- including how many portals, ships and chests are in each base -- and changes nothing in the world; the first ward base can only begin after the ward grace of 14 days).

Three claims on a piece. A piece (anything a player built: walls and floors, chests with what is in them, beds, portals, ships and carts, crops, signs) can be claimed by a ward that is switched on over it, by a workbench near it, and by its own builder. Each claim has a grace (days since the last of the players who hold it was active, before anything happens) and a fade (days over which the piece then goes), so the total life of an abandoned base is grace + fade:

claim held by grace + fade (defaults)
a ward that is switched on covers the piece (its own radius, 32 m) the ward's owner and the players on its list 14 + 10 days
a workbench within WorkbenchRadius (20 m) the workbench's builder 7 + 10 days
the piece's builder alone (all such pieces of one builder are one camp) the builder 3 + 4 days

A piece is protected as long as any claim on it is within its grace; when the last has lapsed it goes with the base of that claim, over that claim's fade. A workbench holds everything within its reach whoever built it, so an active player's workbench beside an abandoned camp keeps the camp (and a ward the same); the two halves of a portal pair are one thing and live while either end is held. So a builder who plays every day keeps their piece standing inside a ward whose owner is long gone, and an abandoned base lives 24 days under a ward, 17 under a workbench, 7 as a camp. A ward that is switched off protects nothing, as in the game; a ward or a workbench that is taken down no longer holds its pieces. A base is told apart by the builder and the place of its ward or workbench (the game's own object numbers change every time the world is loaded, so they cannot be kept).

A player counts as active when they have been in the game ActiveMinutes (10) in one go, so a quick login to see whether the base is still there does not keep it for ever; and nothing starts or is taken down while one of the players who hold the base is in the game, even before their ten minutes are up (they are told it is about to start); nor while a player is connecting (the pass waits for the next one), nor while a player stands within 30 m of a ship or cart that is due, nor while a piece that is protected rests against it (the game would break that piece too). At most five bases begin in a pass. A player the server has not met counts as active from the first time it sees their builds, so switching decay on takes down nothing at once. The character is what counts, not the account: a second character is another player.

The clock counts only the time the server runs under decay (the real time of the first run plus the uptime kept in the file; it stands still while Mode is Off), so a week of maintenance, a restore or a host whose clock jumps takes nothing from anyone's grace.

What happens. When the last claim on a base has lapsed an episode begins: every piece of the base gets the time at which it goes, spread over the fade. The ward is first, within a day (a tenth of the fade), because it is what keeps others from touching a base, and a day is time to put right a mistake -- a setting, an old file -- before it is gone for good. Then the highest pieces (roofs, then walls, then floors, by height above the ground), then what holds things (chests, beds, tables), the workbench last. The time is written to the piece itself (sgg_decay_at, and sgg_decay_ep naming the episode), so a base does not lose its protection level when its ward goes. The pieces that go within a day are worn: their health is cut to a fifth, so the game shows them broken, and a hammer can still repair them. A piece goes without a trace -- no debris, and what is in a chest goes with it. At most MaxRemovals (200) pieces go in one pass; the world is looked through every ScanMinutes (10), and when a player has just joined.

Coming back. Every pass the marked pieces are judged again, one by one: a piece whose builder (or a workbench or ward that holds it) is active again is unmarked (a worn piece is whole again), and an episode whose pieces are all unmarked is over. The owner coming back therefore saves their own pieces; the pieces of a guest whose own claim has lapsed go on with the old schedule until a ward is built over them again. What is gone is gone, and a ward has to be built again; a piece that is marked goes unless someone who held it comes back -- a ward a stranger builds over it later does not save it. A base left without its ward is judged again by what holds it now (a workbench, or its builders alone), so a ward that is switched off or destroyed hands its pieces to the shorter claims.

Players are told half a minute after they join (LoginMessage), at most four lines: the bases that are decaying (how long is left, and that staying in the game ActiveMinutes stops it), then those that start soon (within half the grace, at most three days). A dry run says nothing to players.

Who is left out. Nobody, by default. Anyone put on the list by hand (decay exempt) counts as always active for what they built, and so do admins if ExemptAdmins is set (the server learns that a character is an admin when it is online): their pieces, their ward, their workbench, so a spawn hub never decays. An exempt guest on someone else's ward list does not make that ward immortal: on a list only a guest's real activity counts.

Console (the server's standard input): decay lists who is away and for how long, and every base with its stage and where it stands; decay away <player> <days> counts a player as active until so many days from now -- the grace then starts to run, so a holiday of 7 days under a 14-day ward is 21 days in all --, decay away all <days> everybody the server knows, never shortening a longer holiday (a pause for the whole world); decay clear unmarks every piece, mends what is worn and forgets the episodes (the way back from a run that should not have been); decay exempt <player> and decay unexempt <player>; decay scan makes a pass now. A player is a name the server has seen, or the id from the list. Everything it does is written to the log and to <world>.guard.log (decay: ...). The players, their last activity, exemptions, the episodes in progress and the world they belong to are kept in <world>.decay.txt next to the world, every two minutes (the dates in it, in decay and in the log are on the decay clock, which counts only the time the server runs, not on the calendar); a file that cannot be read is left alone and decay stays off until it is fixed.

What it does not do: it does not know who owns an object that has no creator (the world's own, and what an admin spawned), and a modified game can write a creator into an object it places; it cannot bring back what it took down (take a backup, DryRun first); the clock of a character is only what this mod has seen since it was switched on; and a ward that is destroyed some other way than by decay (a raid that gets past the ward guard, the owner taking it down or switching it off, the limit on switched-on wards) makes its base a workbench base or a camp at once -- days away that were within a ward's grace may be past a camp's. When the decay file cannot be read the server must be restarted once it is fixed or removed (a removed file starts everyone's clock again); with the file unwritable nothing is taken down until it can be written; a file made for another world of the same name is set aside as <world>.decay.txt.other-world and decay starts again. Decay and the Resource Regrowth plugin work together: when the last workbench of a place goes, Regrowth treats the place as free.

Anti-cheat

The server does not see a player's mods -- the game tells it nothing about them -- and almost every rule of the game is kept by the player's own game: what may be built where, how fast, what a seed costs, how big a stack may be, whether a character may fly. A modified game just does not keep them, and the server believes what it is told. What the server does see are the results: every object a game creates is sent to it, and it can ask of each one whether an unmodified game could have made it. With [AntiCheat] Enabled it does. It is server-side only, so clients stay vanilla, and it is not a scanner of anyone's computer: a cheat that leaves nothing in the world (a faster run, a free build, a stronger sword, a longer reach) it cannot see.

How. An object that a player's game creates reaches the server in ZDOMan.RPC_ZDOData. Whose it is, is the connection it came in on: the id the object carries is only the sender's claim, and a game that wants to put its work on someone else can write that someone's session into it (which is a rule of its own, Forged). At least 1.2 s later, when the rest of its data has come (at most 400 objects or 3 ms of work per frame), it is judged by rules taken from the game's own code (Valheim 1.0.20): Player.PlacePiece is the only thing that writes a creator on a piece, a piece is aimed at within 5 m of the player's eye, Player.m_placeDelay is 0.4 s, a character is made by the game that plays it, and the tools' build tables are in the item database.

rule an object (or character) of a player's game that ... default
UnknownPrefabs is of a kind the server's game does not have (a content mod, a build planner's placeholders) On
NotBuildable has a creator but is in no build table of the game and is not put down by an item's attack: a thistle, berry bush, mushroom or ore planted with the cultivator, a piece of another mod On
Forged carries the id of another game's session, or writes to the character object of another game On
Identity is a character that names another game's character as its own, or a piece from a game that has no character at all On, warns first
Creator has a creator that is not the character that sent it On
Distance is a piece placed more than MaxDistance (30) m from the player, both when it arrived and a moment later On, warns first
Rate is one of three or more plants of one player within MinInterval (0.25 s): rows and grids of seeds with one press (with PaidPlanting: unless the player's stamina paid for them) On
Items is a dropped item whose stack or durability is more than the item can have, or whose quality is above MaxQuality when one is set (the game's upgrade station has no ceiling; an egg or a fish carries the level of the creature) On
CheatMarks is a creature that carries the game's own mark for cheating (the console's spawn) On
Fields carries HasFields, per-object field overrides (invulnerable, invisible, enormous), which the game writes only for the console's spawn given component fields On
Level is a creature of a level above MaxLevel (10): a spawn menu lets a player type any level On
Fly is a character whose fly switch is on On
Flood is one of thousands waiting to be looked at from one player (more than the game makes in minutes: past 4,000 the rest are not looked at, and that player is acted on; when it is the whole queue that is full nobody is blamed) On
Wards is a piece inside a ward that is switched on, built by a player who is not its owner or on its list (the ward guard's rule) On, warns first
NoBuild is a piece where the game lets no one build On, warns first
Biome is a piece that belongs to one biome, in another (judged only where the server has the ground loaded, and only if the world generator says the same, so near a border a piece is let through) On, warns first
Dungeon is a piece that may not be built in a dungeon, built in one On, warns first
CheatMarksOther is anything else with the cheating mark (a piece built from items an admin handed out has it too) LogOnly
Cultivated is a crop on ground that is not cultivated (the server has the ground only near players, a moment behind) LogOnly, warns first
Burst is the 15th piece from a player in three seconds (the game places one in 0.4 s; a stalled connection that delivers everything at once looks the same) LogOnly
Health is a piece with more health than its prefab can have (skill mods make pieces tougher on purpose) LogOnly
Rotation is a piece tilted, or not turned in a multiple of 22.5 degrees (the free-rotation mods; a piece the physics moved, too) LogOnly
Teleport is a character that moved 250 m between two looks (every 2 s; looks 1 to 8 s apart count) with no portal within 20 m of either end, neither in a dungeon LogOnly
Speed is a character that moved faster than 30 m/s sideways in two looks in a row LogOnly

What happens. A rule that is On does what Action says: Log writes the finding down, Revert also takes the object away (the server destroys it; whatever a chest or a stand held goes with it, nothing is refunded), Kick also tells the player what was wrong in the middle of their screen and removes them from the server six seconds later. The rules marked "warns first" depend on the state of the server a moment after the player's game decided (a ward switched on just then, ground that was not loaded yet, a character that had just died): the first time one is broken in ten minutes the object is taken away and the player gets WarnMessage; breaks less than 15 seconds later are the same time (one mistake can be several objects), and the second time (SoftStrikes), 15 seconds or more after, they are kicked. All the others kick at the first finding. Fly, Flood, Teleport, Speed, Burst, Health and Rotation (and Identity, when it is the character that is wrong) have no object to take away: with Action = Revert they only write. A rule that is LogOnly only writes. Admins (adminlist.txt) are not held to any of it (ExemptAdmins; nothing of theirs is even queued): they have the console and the tools that bend the rules. Kick is a removal, not a ban: the player can come back, and everything that breaks a rule is taken away again; the log line names the account, so ban on the console keeps it out. Every finding goes to the log and <world>.guard.log (AntiCheat: ...): who, which rule, which object, where, why; the first of a kind for a player at once, the rest counted together every ten seconds, at most 120 lines a minute. An object that another game has written to since it arrived (a player who opened a chest a moment after it was placed does, as the game hands the object over) is judged as well, but nobody is warned or kicked for what is wrong with it.

Before you switch it on. Set Action = Log first: every rule only writes, and after a few days the AntiCheat: lines in <world>.guard.log show what your players' games do -- there should be none from honest ones, and what there is tells you which mods they run. Then Revert, then Kick. A mod that adds pieces or items of its own (OdinArchitect, SeedBed, WackysDatabase, MyDirtyHoe, a content pack ...) makes objects the server's game does not have: a player who runs one is kicked at the first object it makes (UnknownPrefabs). If the server itself has the mod, its objects are known and the rule does not fire; for a mod that only the player has, the rule would have to be Off. Mods that every player is meant to have, or that change the game for everyone, trip rules for everyone: set the rule to Off or LogOnly -- PlantEverything (NotBuildable, UnknownPrefabs, Cultivated), a shortened tool delay such as ToolTweaks' (Rate, Burst), stack-size mods (Items), build-restriction mods (Wards, NoBuild, Biome, Dungeon, Distance). Take a backup of the world before anticheat revert.

Where the numbers come from. MinInterval and RatePlants are set from the real planting times of three players of the live world: 181 and 131 plants put down by hand (never three within a quarter of a second) and 110 put down in groups by a mod (up to 36 within a quarter of a second); the two who plant by hand were not caught, the mod user was, plant by plant. The data is in docs/TESTS.md; a mod that shortens the pause between presses (ToolTweaks) can put honest plants closer than that.

Paid planting. PlantEasily, MassFarming, ComfyMassFarming and CropUtils plant a row or a grid of crops with one press. With PaidPlanting = true (off by default; Rate must not be Off) a group of plants that Rate would take is let through when the player's own game paid for it as the game does: the cultivator was in the hand and the character's stamina went down by PlantStamina (1.3) for every plant. How the stamina is read, what each of those mods charges, and what the server cannot see of it: docs/ANTICHEAT.md.

The stored world. The console command anticheat scan looks through everything in the world with the rules that do not need a player; anticheat revert takes away only what no tool of the game makes and dropped items above their maximum (it ignores Action and cannot be undone, and scan first shows exactly what it would take); anticheat alone says what has been found since the start. What each looks for and what it leaves alone: docs/ANTICHEAT.md.

The popular cheat mods. Which mods leave what in the world, and which rule sees it, is tabulated in docs/MODS.md: from the READMEs of 65 popular cheat and tweak mods on Thunderstore and the decompiled code of 29 of them (October 2026) -- planting (PlantEverything, PlantEasily, CropUtils, MassFarming), build restrictions (BuildRestrictionTweaks, NoBuildRestriction), flight and teleport, spawn menus (EasySpawner, Infinity Hammer), stack sizes and item spawners, and the mods that change nothing the server can see (speed, tool wear, free pieces). The short of it: what a mod leaves as an object in the world is seen, what it changes inside the player's own game is not.

What it cannot see. Anything the game does inside the player's own game -- how fast a character runs, how hard a weapon hits, what a tool or a recipe costs, how much can be carried, how long a pickaxe reaches -- and a cheat careful enough to look like a player. The rest, and how the rules can be got round: docs/ANTICHEAT.md.

Tested on a copy of the live world with 30 simulated players -- a fair builder, an admin and one cheater of each kind -- and a real Valheim client joining, walking and jumping against the same server: every object that broke a rule was taken away and the player kicked after six seconds, nothing else was touched, nobody who played fair was kicked; PaidPlanting with 29 simulated players, 29 of 29 checks. What was not tested: a real client placing pieces and planting, and the mods themselves. Everything that was tried: docs/TESTS.md.

Location icons

Some locations carry an icon for the map: the traders (Haldor, Hildir, the Bog Witch), Hildir's three dungeons, the Deep North boss room and the ancient upgrade station; the start temple's is always shown. Vanilla sends every player the icon of each of them as soon as the location is placed -- when its zone is generated, which happens whenever anyone passes within a few zones, usually without anyone seeing it -- and the map draws it whether the player has explored there or not (ZoneSystem.SendLocationIcons goes to everyone; Minimap checks no fog for it). So the first player to pass near Haldor's camp gives everyone his position.

With [LocationIcons] Enabled, the server sends each player only the icons of the locations they have been near themselves, also from inside a dungeon above or below, and remembers them per character (<world>.icons.txt, one line per character and location). How near is DiscoverRadius: 0 (the default) is as near as the game itself shows an icon -- the location's zone is one the game generates around that player (ZoneSystem.CreateGhostZones: the simulation distance, near + far zones of the player's zone, a square unless the distance is not "classic"; 4 zones by default, about 256-320 m straight ahead, more on a diagonal). That is the moment vanilla shows the icon the first time, to everyone; here the player who comes that near gets it, and every later player when they come as near. A number above 0 is metres. A player who finds one gets its icon within two seconds. Icons the game always shows stay for everyone, and players' own pins are not touched. Tested with two simulated players on a copy of the live world: far away both were sent only the start temple; the one taken to the ancient upgrade station got its icon too, the other did not; after a restart the first one got both icons at once.

Fire control

How fire spreads, from the game's own code and data (Valheim 1.0.16). Outside the Ashlands all of it needs the Fire world modifier (-setkey fire); in the Ashlands fire always works. Two ways a fire starts, both only from something lit:

  • Sparks. A bonfire or campfire throws one every 5 s (50 % chance each time), a fire burning on a piece every 2 s and one on the ground every 5 s (30 %), in a random upward-biased direction, under gravity alone. On a burnable piece that is not wet, a tree or a log a spark always starts a fire; on uncleared grass, outside the Mountains and Deep North and not in rain, with a 32 % chance.
  • Direct ignition. A lit fireplace sets alight whatever burnable is inside a small capsule around its flame, every 5 to 10 seconds.

Every fire carries a generation count: it throws sparks only while the count is above 0, and what its sparks light gets one less. What a fireplace lights directly gets the fireplace's own count. How far each source reaches -- spark landing distances simulated with the game's own spark code, 400 000 sparks each, on flat ground:

Source Sets alight directly Its own sparks land at most Spark flights the chain can make Farthest on flat ground
Bonfire 1.7 m around, up to 6.2 m above 2.9 m (half within 2.3 m) 4 about 17 m
Campfire 0.45 m, up to 1.6 m high 1.0 m 2 about 8 m
Hearth 1.4 m along the world's east-west axis, 0.8 m across, up to 1.7 m high (the capsule does not turn with the hearth) -- 4 about 17 m
Iron fire pit 0.7 m -- 4 about 16 m
Floor and ceiling braziers 0.3 m; a floor brazier reaches the floor under it -- 1 about 4 m
Standing torches (iron, wood, blue, green) 0.1 m around the flame -- 1 about 4 m
Resin candle 0.1 m, 0.25 % every 25 s -- 1 about 4 m
Sconce, lanterns, jack-o-turnip, snow lantern, NPC fire pits, forges, kilns, smelters never -- -- --
Fire arrow -- 3 sparks on impact, 2.9 m 4 about 15 m
Staff of Embers fireball -- 6 sparks on impact, 5.0 m 4 about 17 m

A fire's sparks fly at 5 m/s and land up to 3.9 m away on flat ground, farther from a height: 5.1 m from a fire 2 m up, 6.2 m from 4 m, 7.8 m from 8 m, 9.1 m from 12 m. "Farthest" adds up the longest flight of every generation, so it is a ceiling: each one has to land on something burnable, in the right direction, before its fire goes out. Ashlands meteors and the summoned troll throw sparks too.

A fire burns for at most 30 seconds (the game's own timer on every fire), and a spark that has not landed within 3 s is gone; in its 30 seconds a fire on a piece throws about four or five sparks, one on the ground about two. It goes out sooner when rain falls on it with no roof above, when what it burns on is destroyed, when a smoke bomb goes off within 3 m, or when it chokes on smoke: under a roof, more than 7 puffs within 2.9 m of a point 3.2 m above it. In a house that is the smoke of a hearth or fire pit; a fire's own smoke source stops puffing as soon as its smoke has nowhere to go -- every smoke source counts itself blocked with smoke within 0.75 m of it (0.4 m for braziers), and a fireplace whose smoke source has been blocked for 4 s goes out until the air clears (see [Fixes] ServerSmoke).

On a dedicated server the weather that puts fires out, and makes pieces too wet to catch, is the Meadows' wherever the fire is: the server has no camera to follow, so the game falls back to the Meadows for it, in the Ashlands and the Swamp as much as anywhere.

Vanilla has no limit in metres; [FireControl] adds one. Every spark and fire remembers the source its chain began at (a lit fireplace, or where an arrow, fireball or meteor came down; kept in its ZDO, so it survives a restart), and with MaxRadius set, a fire that would start farther than that from its source does not start. MaxSpread caps every generation count. FireplaceIgnition = false stops direct ignition entirely: a hearth, brazier or torch then cannot start a fire at all, while a bonfire or campfire still throws sparks.

OnlyProjectiles = true goes further: only a projectile starts a fire. Every spark and fire also remembers whether its chain began at a projectile (in its ZDO), and only those throw sparks; no fireplace sets anything alight or throws a spark of its own. Which attacks carry fire was read from the game's data: a fire arrow (3 sparks where it lands), a Staff of Embers fireball (6), Ashlands meteors and lava rocks, and the sparks of the Ashlands sky. The fireballs of surtlings, fuling shamans and the Charred mages, and Fader's fire breath and wall of fire, deal fire damage but never set anything alight in the game.

Who simulates a fire. Sparks fly, and fireplaces ignite, only on the machine that owns the object, and the server only takes over what nobody owns or what its owner has left. What a player builds or sets alight is created and owned by their own game: without more, a hearth a player has just built, or the fire their arrow started, would burn by the game's rules on that player's computer until they left the area or the server restarted -- in 1.13 and 1.14 none of the settings above reached them. Now the server takes every fire source (every fire, and every fireplace that ignites or throws sparks: 15 kinds) the moment a player's game reports it, whether new, an update from a game that has not heard the news yet, or a fireplace a player merely claimed (pressing E on one nobody owns). That game stops simulating it long before its first spark (2-5 s) or direct ignition (5 s). A fire taken over this way that has no record of its chain was lit by a projectile on that player's computer and counts as one, its own position as the source -- up to 2.9 m from where an arrow, 5.0 m from where a fireball came down, which MaxRadius therefore allows on top. A fire that appears within 8 m of a creature that throws sparks (the summoned troll) is not from a projectile, and with OnlyProjectiles it is put out. The server's copy of a fire finds the piece, tree or log it burns on, so it goes out when that is gone, as on the game that lit it. What stays on the player's computer is their own arrow or fireball and the sparks it throws where it lands.

Dvergr trade

The dvergr of the Mistlands are friends, and killing them to empty a camp feels wrong. A vanilla game can show a trade window only for a trader the game knows (the goods of Trader live in the client), so the trade here is a chest. Everything is on the dedicated server (Valheim 1.0.20); the players need nothing.

  • The stands. Each of the eight dvergr camps (the three Mistlands guard towers, the lighthouse, the two excavations, the harbour and the Ashlands' charred tower) has exactly one dverger_guardstone, the dvergr's own ward. When a player comes within StandRange of one, the server puts up a stand beside it once the camp is loaded: a dvergr chest, which anyone may open even inside the ward, and a sign with the title and one line per offer. A destroyed chest or sign is put up again after ten minutes; the stands are found again after a restart.
  • The trade. Put the price in, close the chest. The server takes the chest back from the player's game, weighs what is in it, takes the price and leaves the goods (new items of quality 1); offers are paid in the order of the list, each as many times as it is paid in full. Goods that do not fit undo that trade, and the player is told. Each trade is logged.
  • The ward. Ward:<hours> as the reward switches the camp's ward off for that long (a second payment adds to what is left, up to 7 days from now) and calms the dvergr near it that were angry. While it is off, picking what lies in the camp does not anger the dvergr; hitting a dvergr still does, and paying again calms them. The time survives a restart.

In full -- the locations' names, how the chest is taken back and weighed, how the ward is kept off, how the game sizes the sign's text: docs/DVERGRTRADE.md.

Not in this: coins, a trade window, prices per camp, taking the stands down when the feature is switched off (they stay where they are). Switching the feature off in the configuration does not end a ward's time: it still comes back on when it is up.

Hosting notes

  • After a game update, look for Vanilla ... changed warnings in the log, and keep world backups.
  • On a shared host with a chiplet CPU (AMD EPYC, Ryzen), CpuAffinity = auto keeps the server on one die, the one least busy when it starts; other servers on the host are not pinned and may still use it. Each server measures for itself, so two started in the same second can pick the same die.
  • Running under AMP (CubeCoders)? Settings and pitfalls (Sleep mode, stop without save, console commands) are in MistrCech/amp-valheim-bepinex.

Caveats

  • Only runs on dedicated servers.
  • Uses considerably more server resources than vanilla; a weak CPU or little RAM may make play worse, not better.
  • Disable the mod when using the optterrain command.
  • It does not prevent cheating or any kind of client manipulation.
  • Game updates can break it in unexpected ways; back up characters and worlds before updating.

How it works

Ordinarily, to keep server resource usage low, the Valheim server hands off simulation of an area to the first client that enters it. This mod makes terrain, monsters and other objects that are normally created and owned by clients be created on — and thus owned and simulated by — the server, around every connected player.

For mod developers - compatibility

This mod keeps the plugin GUID of Serverside Simulations, MVP.Valheim_Serverside_Simulations, so existing checks for it keep working.

If your mod changes the simulation or behaviour of the world, it has to be able to run on the dedicated server:

  • Player.m_localPlayer is always null on a dedicated server; check for it.
  • On a dedicated server, ZNet.instance.GetReferencePosition() returns a position outside of the world, unrelated to any player.
  • Graphics or HUD code should be behind a ZNet.instance.IsDedicated() check if it can run on the server.

Credits

  • Serverside Simulations by ddormer and its contributors: this is a fork, and the simulation itself -- the zones around players loaded and simulated on the server, the configuration and the feature framework -- is their work. The original is at ddormer/valheim-serverside and on Thunderstore as mvp/Serverside_Simulations.
  • @mreastman: the port to Valheim 1.0 (ddormer/valheim-serverside#118) this fork builds on.
  • @Merl: found why the frame rate patch broke on Valheim 1.0.14 (#1); the refresh rate idea is in 1.11.0.
  • BetterNetworking: the server-side network limits are its idea.

License

Our own work is MIT, see LICENSE. A few files carry code from Serverside Simulations by ddormer, which has no licence; that code is not covered, see NOTICE.md.

Building

Create src/Environment.props pointing at a Valheim dedicated server install that has BepInEx:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <!-- Needs to be your path to the base Valheim dedicated server folder -->
    <VALHEIM_DEDI_INSTALL>E:\SteamLibrary\steamapps\common\Valheim dedicated server</VALHEIM_DEDI_INSTALL>
  </PropertyGroup>
</Project>

Then, from the repository root (Windows or Linux, tested with .NET SDK 10):

dotnet build src/Valheim_Serverside/Serverside_Simulations.csproj -c Release -p:SolutionDir=<repository root>/

The DLL ends up in bin/Release/. SolutionDir is needed when building the project on its own; building Valheim_Serverside.sln sets it.

CHANGELOG

[1.29.1] - 2026-11-04

Fixed

  • Decay: under one ward there can be two claims, the builder's own pieces (an exempt builder is never away) and the pieces of others (held only as long as the keepers really play). The pass gave every piece under a ward the claim of the first piece it came to, so whether the base of the strangers' pieces began at all depended on the order of the pieces in the world: with the ward's piece first nothing of theirs began, with a stranger's first the exempt builder's own pieces were marked as well (and unmarked again in the next pass). A base is now the pieces that share a claim (its key and the time it lapses): two bases, and the same result in any order. Nothing changes where no builder is exempt: the live world with the live configuration gives the same 521 lines of report as 1.29.0.

Changed

  • Decay: the pass over the world no longer runs in one go. The tick works through a copy of the list of objects in slices of some 4 ms and gives the frame back in between (before: 20 ms a pass, 80 ms the first one after a start, on a world of 891,000 objects). Who is in the game is read again after the slices, before anything is judged or started. The console's decay scan, a login that finds the status old, and the tests still run the whole pass at once. The log line of a pass now says how many slices it took and how long the longest was.

[1.29.0] - 2026-11-03

Added

  • Web map ([WebMap] Enabled, off by default): the server writes the files a map of the world can be made from, for a program of yours to serve. Nothing is sent anywhere, and no map or web page comes with it: anyone can build one on the files. live.json, every 30 s and the one file for the public: the players online with their positions (by default only those who show their position on the game's map, OnlyPublicPositions), with PortalPrefix the portals whose tag begins with it, with Bases the player-built pieces counted per 512 m square -- no builder, no exact place -- and an id of the world, not its seed. places.json, private, every 5 minutes: the seed, every location of the world, placed or not, and the bosses beaten. explored/<account>.bin, private: the part of the map each account has been through. That lives only in the player's game and never reaches the server, so the server makes it again from where the character is, every 2 s, with the disc the game itself marks (9 cells of 12 m); it starts when the feature does, and what a cartography table gives is not counted. By default the public file lists only players who show their position, no portals and no bases, and goes to the folder public inside the private one. Files are written by a background thread through a temporary file; a map is held in memory only while its player is online. On a copy of a world of 85,788 index entries the count of the pieces took 8 ms in all, 3 ms the worst frame. Formats, how to draw the files and limits: docs/WEBMAP.md. Needs a restart.

Changed

  • The default greeting of [CharacterGuard] Welcome (off by default) names no server and describes no one's mechanics: the title is Welcome and there are no lines. It used to say "Welcome to Sarkastic.gg" and to explain how zones grow back and how fire spreads on that server. A config that already holds a text keeps it: only a config without the keys gets the new defaults.

[1.28.0] - 2026-11-01

Added

  • RPC census ([RpcCensus] Enabled, off by default): every RPC a player's game sends that the game itself does not have -- the mark a mod on their client leaves on the wire -- is noted: the first time in a run with a line in the log, at the end of a visit with a summary of it, and all of it in <world>.rpcs.txt next to the world (player, call, to whom, on what object, how many times, bytes, first and last seen, a few readable words out of it). Calls are named from a table of the game's own 183 and 468 of mods, read out of the code of the 300 most downloaded mods on Thunderstore; one without a name is written as its hash. Nothing is blocked or changed. Needs a restart.

[1.27.1] - 2026-11-01

Changed

  • The ward guard, the portal lock, the guardian stones and the decay no longer look through every object of the world to find what they need. Each did that in a single frame: the ward guard every 5 s, the portal lock every 10 s, the guardian stones every minute (35-45 ms each time on the live world, 891,000 objects), and the decay at every pass. A world index now finds the wards, the portals, the guardian stones and everything that can have a builder once, after the world has loaded, and keeps them up to date as the game makes and takes away objects. Every 10 minutes it is checked against the whole world, at most 20,000 objects a frame, and whatever it had wrong is put right and written to the log. The log line "Ward guard: N ward(s) among ... objects, found in ... ms" is replaced by "World index: ...".
  • A ward, portal or guardian stone that is built counts at once, not at the next look: a stranger no longer sees a new portal's connection for up to 10 s after the ward over it was built.
  • Decay: a pass reads only the objects that can have a builder (on the live world 86,000 of 891,000), and finds the wards and workbenches over a piece in a grid instead of trying each of them for every piece. The first pass after a start still reads every object, to check that nothing but pieces has a builder; should anything else have one (set by another mod), every pass reads every object again, as before, and the log says so. What it decides is the same: on a copy of the live world, five passes 0 to 24 days on gave the same report, line for line.

Measured on a copy of the live world with the live configuration, on the test machine (where the old looks took 14-18 ms), over 640 s: what the plugin does in its frame updates came to 0.98 s instead of 4.42 s, and frames longer than 20 ms fell from 39 to 9 (longer than 50 ms, from 8 to 3). The ward guard's 14 ms every 5 s are gone; the portal lock takes 0.35 ms instead of 18 ms every 10 s, the guardian stones at most 0.02 ms instead of 16 ms every minute, a decay pass 18 ms instead of 80-97 ms (75 ms for the first one after a start). The world index costs 33 ms once, after the world has loaded, and its check every 10 minutes some 30 ms, spread over 50 frames of at most about 1 ms. On the live server, where the old looks took 35-45 ms, the index took 121 ms to make and its first check 73 ms over 50 frames, at most 3.1 ms in one; it found the index right.

[1.27.0] - 2026-11-01

Added

  • Console commands that act on a player: the server's versions of the game's own, which cannot work on a dedicated server.
    • tp <player|all> <x> <z> (also teleport, goto) sends the player there with the game's own teleport. That is the message vanilla's recall sends, so a vanilla game obeys it. The height is 30, the one the game's own goto uses. About 15 s later the game puts the player on top of whatever stands there (the ground, a roof, a rock); at sea, in the water. The other forms:

      • tp <player|all> <x> <y> <z> uses that height; after 8 s the game puts the player on the floor below it.
      • x,z or x,y,z may be written as one word, as players and pos print them.
      • tp <player|all> start sends the player 2 m above the middle of the start, where a new character first appears.
      • tp <player|all> to <player> sends the player where that player stands, facing as they do.

      A name may contain spaces and may be a unique beginning; all is everyone online, but the player sent to. The console says whom it sent from where. Once the character is there, it says where and with what health. If the character is not there after 40 s, it says so: their game did not move them, because the player is dead, in a menu or already teleporting. A game takes the first of several teleports sent at once and refuses the rest.

    • pos [player|all] (also where) prints where each player is, the biome (or "in a dungeon") and their health.

    • heal <player|all> [amount] heals to full health, or by that amount. It heals health only: the game has no message for another player's stamina. A dead player is not healed.

    • spawn <prefab> [amount] [level] <player> does what the game's own spawn does in front of its own player, 3 m in front of that player: level 1 to 9, at most [Server] MaxSpawnAmount (new, 20) at once, marked as cheated as the game marks its own, so what such a creature drops is cheated too. Items go through give; players cannot be spawned.

  • [AntiCheat] Teleport and Speed do not count the console's teleports. For a minute after the console sends a player somewhere, the character's moves within 50 m of any spot it was sent to are not judged.

[1.26.0] - 2026-11-01

Added

  • [PortalLock] Protect (default false) and ProtectMessage: who may hit a portal or take it down with the hammer. Where a ward holds the portal (it covers it and has its builder on its list, switched on or off -- as for who may use it), the ward's owner and the players on its list; where no ward holds it but one holds the other end of its pair, that ward's list too, so the far end of a base's pair out in the open can no longer be smashed to cut the base off; where no ward holds either end, anyone, as in the game. Admins always (ExemptAdmins). A refused hit (RPC_Damage) or removal (RPC_Remove) is not passed on to the game that owns the portal, or not handled where the server owns it; the player is told (ProtectMessage, at most once a quarter of an hour per portal) and <world>.guard.log gets a line (once a minute per player, portal and kind, with a count). A hit from a player's game counts whatever made it -- a weapon, a catapult or a battering ram (their hits name no attacker), fire; only a hit by a creature that game runs (a raid) goes through. Repairs are not held back. In the game a ward keeps only the hammer off what stands in it, and nothing guards a portal out in the open.
  • The vanilla drift check also watches WearNTear.Damage and Remove, which Protect relies on to send both requests to the game that owns the piece.

[1.25.0] - 2026-10-30

Added

  • [DvergrTrade] OffersAshlands: the dvergr of the Ashlands' charred tower (CharredTowerRuins1_dvergr) have a price list of their own, on the sign and in the chest: TrophyCharredMelee:3 > Grausten:20, TrophyAsksvin:2 > FlametalOreNew:5, TrophyMorgen:1 > MoltenCore:2, TrophyVolture:1 > Lantern:1, TrophyFallenValkyrie:1 > Ward:24, written as Offers. Up to 1.24.0 every camp had the one list, so a camp in the Ashlands sold black marble and black cores for seeker trophies. A camp goes by the land its location is placed for, not by the ground under its ward. Empty, or not one offer in it valid (the log names what was skipped): Offers holds for the Ashlands too. A sign standing since 1.24.0 is rewritten when a player next comes near. A warning about an offer names its list, and one in both lists is given for each.

[1.24.0] - 2026-10-24

Added

  • [DvergrTrade] (off by default): buy from the dvergr of the Mistlands with trophies, and have a camp's ward switched off, with vanilla clients. Each of the eight dvergr camps (the three guard towers, the lighthouse, the two excavations, the harbour, the Ashlands' charred tower) has one dverger_guardstone; when a player comes within StandRange (48 m) of it, the server puts up a stand beside it once the camp is loaded: a dvergr chest and a sign with the offers (ZDOs marked sgg_trade 1 and 2, found again after a restart; a spot found with physics at 2.5 to 5 m; a destroyed part is put up again after ten minutes). What a player puts into the chest and closes is weighed against Offers (price > reward, e.g. 3 seeker trophies for 20 black marble): the server takes the chest back from the player's game once it is closed (the game gives an opened chest to the player and never returns it), takes the price, leaves the goods, and tells the player; goods that do not fit undo that trade. A reward Ward:<hours> switches the camp's ward off for that long (sgg_ward_off_until in the ward's data; the game switches a dvergr ward on again whenever its area is made, so it is kept off every two seconds and by a patch on PrivateArea.Awake), calms the dvergr near it that were angry, and, while it is off, does not pass on the anger of picking what lies in the camp (BaseAI.SetAggravated with the reason "thief"); hitting a dvergr still angers them. Messages: top left to whoever opens the chest (prices, state of the ward), in the middle of the screen after a trade, when nothing is paid in full, when the goods do not fit, and to the players near the camp when its ward goes off and on. Besides the keys in the README, WardOffText, WardStateOn and WardStateOff hold the few words that are not item names, so a server in another language can have them in its own. Switching the feature off puts up nothing new and takes nothing down; wards that are off still come back on in time. The sign is not translated for each player, so SignLanguage (e.g. Czech) gives its item names in one language, the game's own words read from its localization files without changing the server's language; empty, the server's (English). Needs a reference to assembly_guiutils (Localization, LocalizationSettings). Guards from a review before release: the chest carries s_cheated = 1 (Regrowth skips it; the game marks drops as cheated only at 2, so a broken chest's wood is not cheated); a chest taken back is weighed a tick later, and if the player's game granted itself an open meanwhile (its owner revision goes down) it is given back unweighed; for 2.5 s after the server wrote a chest an open is answered "in use" (a game would show and save back the old contents); an offer of the ward is taken only while all its hours fit in seven days (else MessageWardMax, the trophies stay); goods that do not fit stop the item offers after them but not the ward's; a ward that is off takes no damage. The sign is lifted onto the ground (its pivot is the middle of the board). A count and its item are joined by a no-break space (3× Seeker trophy), so the messages' wrap does not put them on two lines; each line of the sign is in <nobr>, since the game sizes a sign's text to fill the board and so grew it until a line broke ("... 24" over "h").

Changed

  • README: the details of the anti-cheat (paid planting, the stored world, what it cannot see; docs/ANTICHEAT.md) and of the dvergr trade (docs/DVERGRTRADE.md) moved out of it, with summaries and links left behind: with the dvergr trade it had grown to 103,505 characters, and Thunderstore takes at most 100,000.

[1.23.3] - 2026-10-24

Changed

  • [AntiCheat] Items: a dropped item's quality was limited to 6 (or the recipe's top, whichever is more). Valheim 1.0's upgrade station takes quality one step at a time past the recipe's top with no ceiling of its own, and the upgraders it needs drop from the chests of every biome, so an honest player's item of quality 7 would have been taken away and the player kicked. Quality is now checked only against the new [AntiCheat] MaxQuality (0 = not checked, the default); stack and durability are checked as before.

Added

  • Night spawn guard: a spawn gated by a world key outside the boss chain the guard knows (Deep North's jotun_killed) is logged once and left as the game has it; before, nothing said so.
  • Guardian stones: a stone whose power is not in that chain is logged (its trophy is shown to everyone); before it was silently left out.
  • Portal lock: each KeepClear name the world has no location of is logged once (a world generated before an update, or a misspelt name); before, only a world with none of them was.
  • Two more game methods made safe on the server (ServerSafe): Trap.RPC_OnStateChanged (the Fuling and troll traps read the local player on every state change: a null reference on the server for every trap it owns) and MusicVolume.RPC_PlayMusic (skipped on the server; music means nothing there). Found by a scan of the game's RPC handlers for Player.m_localPlayer; neither had shown in a live log yet.

[1.23.2] - 2026-10-18

Added

  • A message in the middle of a player's screen is held. The game shows it at full strength and fades it out over 4 seconds from the moment it arrives, so a message of several lines was half gone before it was read. The server now sends it again every second, [Server] MessageSecondsPerLine (3) seconds for each of its lines, at most 30 in all, and the fade starts again each time: the anti-cheat notice (5 lines) is held 15 seconds, a 2-line broadcast 6. A new message to a player takes the place of the one held for them, a broadcast replaces what is held for everybody, and a player who has left is dropped. Each repeat is one more entry in the player's message log (Compendium, Logs; the last 50 are kept). 0 = shown once, as the game does. The top left corner is not held (its messages queue and fade one after another). The repeats keep real time, not Time.time: a loaded server's game time falls behind (a frame counts for at most a third of a second; 15 s of it took 20 on the test server). Tested on a dedicated server with six fake players: a message of one line arrived 3 times about a second apart, one of three lines 9 times over 8 s, a message that replaced it cut the old one off, a broadcast of two lines reached everybody 6 times (the broadcast once, five repeats) and a player who was told something else in the middle got nothing more of it, a player kicked in the middle got one more while the game was still disconnecting them (it takes a second) and then nothing, the top left corner got one, and 0 sent one.
  • broadcast in the server console takes \n (a line break), \u010d (the letter with that code) and \\ (a backslash); any other backslash stays. A panel such as AMP passes a console line in the host's OEM code page and replaces the letters that page lacks by plain ones before the server sees them (č ř š ž ů ě came out as c r s z u e), so these could not be written at all, and a verse could not be broken into lines. Tested through the server's real standard input: bytes of code page 850 with \u016f and \u017e in the line came out as "Skilinův zadek, a line break, nemytý ží" and were held 6 s; a valid UTF-8 line, \ and a backslash that is no escape (\t, \u12, \uZZZZ) came out as typed.

Changed

  • A kick that follows a message waits until the message has been held: the anti-cheat (6 s) and the character guard (8 s) kick after the longer of their own delay and the message's, so the notice can be read to its end. Whatever the anti-cheat judges meanwhile is still taken away at once. Tested: the character guard kicks 8 s after a message of one line and 30 s after one of 12 lines (the most a message is held); the anti-cheat kicked a fake player 16.0 s of the game's time after its notice of 5 lines (15 s), having told it 15 times, the last 14 s after the first. The anti-cheat test of 30 fake players (41 checks) passed twice: with MessageSecondsPerLine = 0 the kicks came 6 s after the notice, as before, and with the default 16 to 19 s after it.
  • [Server] ConsoleInputCodePage 852, now the default (it was 1250). AMP on this host writes code page 850; its á é í ó ú ý and capitals have the same bytes in 852, and 1250 read them as ě ˇ and so on. A config that has ConsoleInputCodePage = 1250 keeps it. The line in the log that tells which bytes arrived now says what AMP does with the other letters.
  • A broadcast is logged and answered with its line breaks written \n, and with how long it is held.

[1.23.1] - 2026-10-17

Fixed

  • Messages on players' screens are broken into lines: [Server] MessageLineLength (50) characters in the middle of the screen, twice as many in the top left corner. A vanilla client never wraps them (a TextMeshPro without wrapping, Averia Serif at 40 in the middle, 20 in the corner), so a long one ran off both sides of the screen -- the anti-cheat notice (218 characters), the ward limit and portal pairing messages, a long broadcast or welcome line. Measured with the game's own fonts on a dedicated server: the 72 characters of the portal lock message take 1200 units in the middle, and the screen is 1920 units wide at the default GUI scale. Every message the plugin sends goes through one place now (PlayerMessages.Show); line breaks already in a text are kept. Tested on a dedicated server with the game's font: every message of the config now takes at most 927 units a line in the middle (the anti-cheat notice 5 lines instead of one of 3921 units), and a broadcast and a message to a player arrived with the line breaks.

[1.23.0] - 2026-10-17

Changed

  • [LocationIcons] DiscoverRadius 0, now the default: a player finds a location's icon as near as the game itself shows it -- once the location's zone is one the game generates around that player (ZoneSystem.CreateGhostZones: within the simulation distance, near + far zones of the player's zone; a square unless the distance is not "classic"; 4 zones by default, about 256-320 m straight ahead). That is when vanilla shows the icon the first time, to everyone; here to the player who comes that near, and to every later player when they do. With 50 m a player who went to a trader someone else had found saw nothing on the map until within 50 m of it. A number above 0 is still metres; a config that has DiscoverRadius = 50 keeps it. Tested on a copy of the live world: a player 257 m (4 zones) from the Black Forest trader got the icon and a second player who had not been there did not; at 321 m (5 zones) nobody got it, and with 50 m nobody got it at 257 m.
  • The vanilla drift check also watches ZoneSystem.CreateGhostZones, which DiscoverRadius 0 copies.

[1.22.1] - 2026-10-17

Fixed

  • Item ledger: part of a pile picked up was never counted in. When the bag has room for only some of a pile (a full bag beside a heap of ore), the game merges that part into the bag and writes the smaller stack back to the item on the ground; there is no DestroyZDO, so the ledger saw nothing and later reported the ore as items the character "never got" (put into a smelter 1 TinOre it never got, which is what the 6 TinOre in a row of one player looked like). The smaller stack from the player who owns the item, within 10 m of their character, is now counted as a pickup of the difference. Tested: 15 of a pile of 40 picked up and 15 put into a chest -- reported before, silent now; the 16th is reported in both. Nothing is taken away by this; the ledger stays LogOnly where it was.

[1.22.0] - 2026-10-17

Added

  • [AntiCheat] PaidPlanting (off by default; Rate must not be Off) and PlantStamina (1.3): a row or a grid of crops put down with one press (PlantEasily, MassFarming, ComfyMassFarming, CropUtils) is let through instead of being a break of Rate when the player's own game paid for it as the game does. The server sees one of the three prices of a plant (seed, wear of the cultivator, stamina): the stamina of a character is in the character object, with the item in its hand. For a group of plants (RatePlants inside MinInterval, as before) it checks that the cultivator was in the hand and that the stamina went down, by the sum of its falls from the moment the first plant arrived to half a second after the last, by PlantStamina for each plant (the game takes 1.625 at the least, times the world's stamina rate). The plants the stamina paid for stay, in the order they came; the rest are taken away and the player is kicked, as under Rate. Once a group is a burst it goes on while its plants arrive less than a second after each other (a hitch of the player's game does not split a grid; what the next group pays is not counted for this one); up to four groups per player are followed; a character that another game has written to, and an admin, are not held to it; a character whose stamina ran out inside the group is as paid as it could be (PlantEasily does not look at the stamina for the extras). Stamina values above 400 and not-a-number are ignored, and the credit of one group is at most what the character had plus 15. What it cannot see: why the stamina fell (sprinting, jumping), the seeds and the wear of the tool, and what a game that forges the values writes; the first plant of a press is paid by the game itself, so a group of up to three with free extras passes. By the code of the four mods as of October 2026, PlantEasily, MassFarming and ComfyMassFarming pay at their defaults, CropUtils (a twentieth, by its own discount) pays for a few plants only. Each group is written once to the guard log with what the server saw; anticheat counts groups and plants let through. Tested with 29 simulated players (29 checks), the 41 of 1.21.0 unchanged with the setting off, and a real client's character (its stamina is in the character object, a jump takes 12 at once, regeneration starts a second later).

Changed

  • README: the long accounts of the tests (docs/TESTS.md) and the table of what the popular cheat mods leave in the world (docs/MODS.md) moved out of it, with summaries and links left behind: Thunderstore takes at most 100,000 characters of a README.

[1.21.0] - 2026-10-17

Added

  • [AntiCheat] (off by default; Action Log / Revert / Kick, default Kick): the server looks at every object a player's game creates (ZDOMan.RPC_ZDOData; whose it is, is the connection it came in on, not the id it carries) at least 1.2 s after it arrives, and acts on what the unmodified game cannot make. Rules, each Off / LogOnly / On, with their defaults: On: UnknownPrefabs (an object the server's game does not have), NotBuildable (a creator on something no build table of the game contains and no item's attack puts down: a thistle, berry bush, mushroom or ore planted with the cultivator), Forged (an id with another game's session in it, or a write to another game's character), Identity (a game that names another's character as its own, or sends a piece with no character), Creator (not the sender's), Distance (a piece more than MaxDistance 30 m from the player, at arrival and a moment later), Rate (RatePlants 3 plants within MinInterval 0.25 s: rows and grids with one press; set from the real planting times of three players of the live world), Items (a dropped item with a stack, durability or quality above the maximum), CheatMarks (a creature with the game's cheating mark), Fields (HasFields, which the game writes only for the console's spawn given component fields), Fly (the character's debug-fly switch), Flood (thousands of objects waiting from one player), Level (a creature above MaxLevel 10), Wards (a piece in a switched-on ward by a player not on its list, by the ward guard's rule), NoBuild, Biome, Dungeon. LogOnly: CheatMarksOther (other objects, and items, with the mark), Cultivated, Burst, Health, Rotation, Teleport (250 m between two looks, every 2 s, with no portal near), Speed (30 m/s). Distance, Wards, NoBuild, Biome, Dungeon, Cultivated and Identity depend on the server's state a moment after the player's game decided and warn first (SoftStrikes 2, WarnMessage): the first time in ten minutes the object is taken away and the player told, the second time (15 s or more later; one mistake can be several objects) they are kicked. Admins are not held to the rules (ExemptAdmins). An object another game has written to meanwhile is judged but nobody is blamed for it; a character another game has written to is left out of the rules. Findings go to the log and <world>.guard.log (AntiCheat: ..., at most 120 lines a minute).
  • Console command anticheat (what was found since the start), anticheat scan (the stored world: what no tool of the game makes, dropped items and chests above the maximum, plants put down in groups, biome and dungeon; who made what) and anticheat revert [all] (takes away only what no tool of the game makes and dropped items above the maximum: never the contents of a chest, a piece in the wrong biome or a dungeon, or an object the game does not have, and it spares the pieces of admins and, unless all is said, of makers the character guard does not know).
  • README: the rules, how to roll them out, what they cannot see, and what popular cheat and tweak mods leave in the world and which rule sees it (65 READMEs read, the decompiled code of 29 of the mods).

Changed

  • [Decay] ExemptAdmins is false by default (was true): nobody is exempt from decay unless named with decay exempt.

[1.20.0] - 2026-10-16

Added

  • [Decay] (off by default; Mode Off / DryRun / On): base decay. What players built is taken down by the server once the players it belongs to have stayed away long enough, so abandoned bases do not fill the world. A piece can be claimed by a ward that is switched on over it (its owner and the players on its list hold it; 14 days of grace, then 10 days over which it goes), by a workbench within 20 m (its builder; 7 + 10) and by its builder alone (a camp; 3 + 4). It is protected while any claim is within its grace; when the last has lapsed it goes with the base of that claim. The two halves of a portal pair live while either end is held. A player counts as active after ActiveMinutes (10) in the game in one go. Nothing starts or is taken down while one of the players who hold the base is in the game, while a player is connecting, while a player stands by a ship or cart that is due, or while a protected piece rests against it; at most five bases begin in a pass. A player the server has not met counts as active from the first time it sees their builds, so switching it on takes nothing down at once. The decay clock counts only the time the server runs under decay (a week of maintenance takes nothing from anyone's grace). When a base's grace is over every piece is given the time at which it goes, spread over the fade: the ward first (within a day, so a mistake can be put right before it is gone for good), then the highest pieces, then chests and beds, the workbench last; the pieces that go within a day are worn (a fifth of their health). Every pass the marked pieces are judged again, one by one: a piece whose builder (or any other claim on it) is active again is unmarked and mended. Bases are named by builder and place, not by the game's object numbers, which change every time the world is loaded. Players are told at login (at most four lines). Admins' characters (ExemptAdmins, learned online or from the character guard) and those named with decay exempt count as always active for their own pieces, ward and workbench; over other players' pieces their ward or workbench holds only while its keepers really play. DryRun writes what it would do (with portals, ships and chests counted) to the log and <world>.guard.log and changes nothing. New console command decay (who is away, every base and its stage; decay away <player|all> <days>, decay exempt|unexempt <player>, decay clear, decay scan). State in <world>.decay.txt, which also names the world (a file of another world of the same name is set aside); with the file unwritable nothing is taken down. See the README.
  • [WardGuard] MaxActiveWards (2; 0: no limit) and MaxActiveWardsMessage: a player who already has that many wards switched on is refused a further one. Two ways, because the game of the player who has just placed a ward handles the key press itself and the request never passes the server: the ToggleEnabled request is dropped when it does, and every five seconds the server counts the wards that are on and switches off the newest ones of a player who has too many (taking the ward over and writing enabled off), and tells them. Wards that are on when the server starts, or when the limit is changed, stay on. Switching one off, or taking it down, makes room. Needs [WardGuard] Enabled.

Changed

  • The ward guard's description of what it sees is exact now: a request for an object that a player's own game owns is handled in that game and never passes the server. This mod's core patch gives the server the objects near players, so the builds of others are not a thief's to own; a piece a player has just placed stays with their game while they are in the area.
  • Plugin version 1.20.0.

[1.19.0] - 2026-10-16

Added

  • [WardGuard] (off by default): the game's ward enforced by the server. Every check of a ward is made in the acting player's own game, nothing checks it when a piece is hit (the ward only flashes), and smelters, kilns, cooking stations, fireplaces, beds and crops have no check at all. Now every routed RPC a client sends is looked at in ZRoutedRpc.RPC_RoutedRPC on the server and judged, by the player the connection belongs to, with the game's own rule: where an enabled ward covers the place, only its owner and the players on its list may act. Structures: hitting, taking down and repairing a player's build piece (and the ward itself) is dropped, the ward flashes and the player is told; a hit with no attacker or by a creature the sender's game owns is not a player's and is not touched. Interactions: chests and doors with the guard stone flag, stands, fermenters, beehives, incinerators, the map table, and taking the product out of smelters and cooking stations, switching a fireplace off, claiming a bed and picking planted crops; a chest asked to open answers no; putting something in, and traps, are left alone (the player's game has already used the item up; the same request is sent when something steps on a trap). Spoofing: a packet whose sender id is not its connection, and a ward's ToggleEnabled / TogglePermitted for another player's id, are dropped. DestroyRequests (LogOnly by default): requests to destroy what the sender's game does not own are counted and logged, or taken out. Refusals go to <world>.guard.log. The startup drift check watches ZRoutedRpc.RPC_RoutedRPC and RoutedRPCData.Deserialize, which the guard reads by hand. Tested with three simulated players on a copy of the live world, with the guard off for comparison, and with a real Valheim 1.0.20 client hitting a warded wall (untouched while off the list, hit once on it; with the guard off it was taken down). See the README.
  • [PortalLock] KeepClear / KeepClearRadius / KeepClearMessage: no public portal within 100 m (sideways) of the start temple, a boss altar or a trader (Haldor, Hildir, the Bog Witch; every place the world has for each). A player-built portal there does not count as public and cannot be given a public name; admin-spawned portals are not held to it. Portals that were public there stop being public; the log names each.

Changed

  • Updated for Valheim 1.0.20 (the game's own code the mod depends on is unchanged from 1.0.16). The startup check still passes. Plugin version 1.19.0.

[1.18.3] - 2026-10-12

Changed

  • Thunderstore package only (Sarkastic_gg/Dedicated_Simulation): LICENSE (MIT, for the work of MistrCech / Sarkastic.gg) and NOTICE.md (what it does not cover: the parts that come from Serverside Simulations, which has no licence, and third-party components) are in the repository and in the package; the README says so. The plugin is the 1.18.0 DLL, unchanged.

[1.18.2] - 2026-10-12

Changed

  • Thunderstore package only (Sarkastic_gg/Dedicated_Simulation): the README has badges and links (Discord, Patreon) and the package is listed in the "Deep North Update" section. The plugin is the 1.18.0 DLL, unchanged.

[1.18.1] - 2026-10-12

Changed

  • Thunderstore package only (Sarkastic_gg/Dedicated_Simulation): an icon of its own, and the README's credits and companion plugin. The plugin is the 1.18.0 DLL, unchanged; a package's icon can only change with a new version number.

[1.18.0] - 2026-10-11

Added

  • [PortalLock] (off by default): a pair of portals is used only by those it belongs to. Valheim pairs portals by name alone and a connected portal teleports whoever steps into it, so a portal built with the name of someone's unpaired one walks into that base. Now the builders may use the pair, and where a ward holds an end (covers it and has the builder on its list) the owner and the players on that list instead -- one ward at one end decides for the pair, the far end included; wards at both ends: a player has to be on both lists. A name starting with PublicPrefix (*) is open to everyone. A player who may not use a pair is sent the portal unconnected (the connection is left out of that one copy; nothing is changed on the server); two portals of one name are paired only if both builders may use the pair (without a ward: the same player built both); a name is changed only by those who may use the pair; pairs that exist are not touched (usable by both builders where no ward holds either end). A ward holds an end only with the portal's builder on its list, so a ward put up over someone's portal afterwards takes nothing. Messages at the portal tell the player why. Tested on a copy of the world with four simulated players: the same scenario with the lock off shows vanilla (every player is sent every pair, a stranger renames a portal and takes it over, portals with nobody in common pair), with it on only the intended players get each pair, a stranger's rename and forged data are refused, a name written on a ward's list and taken off it again reach the players within seconds. The ward's radius is the game's own, 32 m for guard_stone. On a copy of the live world (11. 10.) there are 4 portals, one pair, all built by one player: nothing that exists changes.

[1.17.0] - 2026-10-11

Added

  • [CharacterGuard] Welcome (off by default), WelcomeTitle, WelcomeLines: a character let in for the first time is greeted a few seconds after joining -- the title in the middle of the screen, then each line in the top left corner (the vanilla ShowMessage RPC, nothing to install). A kicked character gets its kick message instead.

Fixed

  • [CharacterGuard] ChangedAway flagged a new player who picked up a club in the minute before a restart (live, 10. 10.): what a character wears was noted every minute and when it left, but a server shutdown never disconnects anyone, so a change in the last minute looked like play elsewhere. It is now noted at every world save and at shutdown too, and only gear past a level 1 workbench counts as a change -- a club or rags are made anywhere in minutes. Tested on a copy of the world with four simulated players, against 1.16.0 as the control: a bronze sword put on 5 s before quitting and a club are no longer flagged (1.16.0 flagged both), an iron sword never worn here still is.

[1.16.0] - 2026-10-10

Added

  • [LocationIcons] (off by default): a location's map icon -- Haldor, Hildir, the Bog Witch, Hildir's dungeons, the Deep North boss room, the ancient upgrade station -- only for players who have been within DiscoverRadius (50 m) of it themselves. Vanilla sends every player every such icon as soon as the location is placed, which happens when anyone passes within a few zones of it, and the map shows it whatever the player has explored: the first player near Haldor's camp gives everyone his position. ZoneSystem.SendLocationIcons is replaced by a per-player version (also when a player joins and when a location is placed); found icons are remembered per character in <world>.icons.txt and sent within two seconds of finding one. The start temple's icon (always shown by the game) stays for everyone. Tested with two simulated players on a copy of the live world, including a restart.

[1.15.0] - 2026-10-09

Added

  • [Server] CpuAffinity (empty by default: as before): auto pins the server to one chiplet -- on AMD EPYC and Ryzen a die with its own L3 cache, on the first EPYC generation also with its own memory -- the one least busy over one second at startup (measured on a background thread, so startup does not wait), read from the system's own topology: Windows processor dies (GetLogicalProcessorInformationEx), else NUMA nodes, else L3 groups; Linux /sys die, node and cache lists. Only processors the process may already use are considered. Or a list such as 16-31. Windows sets the whole process (SetProcessAffinityMask: no thread can leave it); Linux every thread, read again until no new one appears, and once more 30 s and 5 min after startup for threads that set their own processors (Steam starts three). If anything is unclear -- one chiplet, load unknown, a list that is not one -- nothing changes and the log says why.
  • [Server] ProcessPriority: Normal (as before), AboveNormal or High (Linux: nice -5 / -10).
  • [FireControl] OnlyProjectiles (off): only a projectile starts a fire -- a fire arrow, a Staff of Embers fireball, a meteor, a lava rock -- and what it sets alight burns and spreads as before (within MaxRadius and MaxSpread); nothing a base has (fireplaces, the fires they lit, the troll a player summons) throws a spark or sets anything alight. Every spark and fire records whether its chain began at a projectile (sgg_fire_kind in its ZDO). From the game's data: surtlings', fuling shamans' and the Charred mages' fireballs never set anything alight.

Fixed

  • Fire control reached only the fires and fireplaces the server simulated. What a player builds or sets alight is created and owned by their own game, and the server only takes over what nobody owns or what its owner has left, so a hearth a player had just built, or the fire their arrow started, burned by the game's own rules on that player's computer until they left or the server restarted -- since 1.13.0. The server now takes every fire source (every fire, and every fireplace that ignites or throws sparks) the moment a player's game names itself its owner: a new one, an update from a game that has not heard yet, or a bare claim with no data (Fireplace.Interact on an ownerless fireplace, which never reaches ZDO.Deserialize) -- noted in ZDO.SetOwnerInternal while ZDOMan.RPC_ZDOData runs, taken when it is done. A fire taken over without a record of its chain counts as from a projectile, rooted at its own position, unless a creature that throws sparks (the summoned troll) is within 8 m; with OnlyProjectiles such a fire is put out. The server's copy of a fire finds the piece, tree or log it burns on, so it goes out when that is gone.

An independent review by two readers found that a bare ownership claim was missed, that a fire taken over lost the piece it burned on, that fires of the summoned troll counted as from a projectile, and, in the processor placement, that an unknown load picked a chiplet blindly, that offline Linux processors broke the die reading, that a priority failure could stop the plugin loading, and that malformed lists were read leniently; all fixed above. A player's own arrow or fireball still throws its first sparks on their computer, so MaxRadius counts from the first fire it lit (up to 2.9 m from an arrow's impact, 5.0 m from a fireball's).

Tested on 1.0.16: with OnlyProjectiles a lit bonfire, hearth and floor brazier on a wood deck and two loose fires set nothing alight in 60 s, while a Staff of Embers burst (6 sparks, recorded as projectile) lit the grass twice and those fires threw sparks on. A simulated player's game reported a lit hearth, a fire and a fire arrow: the server took the hearth and the fire at once (the fire as from a projectile) and left the arrow; a newer update naming the player's game the owner, and a bare claim, were both taken back the same moment, with the owner change sent back. A fire reported 2 m from a summoned troll was put out; one on a wood floor found the floor and went out when the floor was destroyed. Processor placement on a Ryzen 9 7900X3D: auto read 2 dies and took the idler one (processors 0-5,12-17); the three threads Steam starts a few seconds later with all processors were moved back after 30 s, leaving all 88 threads on the die. On the live host (Windows 11, EPYC 7551P), a standalone probe with the same code, pinning only itself, read 4 processor dies (0-15, 16-31, 32-47, 48-63), the same 4 NUMA nodes and 8 L3 groups, measured all 64 processors and took die 0-15, 5 % busy; malformed lists were refused with the reason.

[1.14.0] - 2026-10-09

Added

  • [GuardianStones] (off by default): a boss's trophy on its guardian stone, and so its power, only for players who have beaten that boss themselves. A power is taken and kept wholly in the player's own game, so a server can never take one back; instead each player's game is sent the stone as that player may use it: a stone whose boss they have not beaten arrives without its trophy (s_item set to 0 in the stone's own data for that one serialization in ZDOMan.SendZDOs, and put back -- no revision change, nothing saved), and their vanilla client has no power to offer. Their game would offer to hang a trophy of their own on what looks empty, which first asks the stone's owner for it: refused where it reaches the server, whether the server owns the stone (ItemStand.RPC_RequestOwn) or a player's game does (the request is not passed on, ZRoutedRpc.RouteRPC -- that game would grant it); also refused while their game may still hold an empty copy. A stone a player's game took over goes back to the server after 10 s. Data for a stone from a player who was sent it empty is not taken (ZDO.Deserialize skipped, the server takes the stone back and raises its revision). Once a player gets that far, the stone's revision is raised once (while the server owns it), and their game takes the trophy; an empty copy counts as theirs until a newer revision has gone out. No stone is sent to a player until their raid list has arrived (ZDOMan.CreateSyncList), so a veteran logging in at the temple is never sent them empty first. A player standing at such a stone, or refused one, is told why (Message). Progress is read as for the night spawn guard (now shared, Progress). The game's guardian stones set no world key and their trophies cannot be taken down (read from its data). A review by three independent readers found that a stone owned by another player's game let the request through to that game, that dropping the bookkeeping when the same revision went out left a player's game with a stale empty copy, and that a veteran logging in got the stones empty first; all three are fixed above. Tested on a 1.0.16 copy of the live world, all seven stones with their trophies, reading back what each simulated player's connection was actually sent: a fresh player got every stone empty, a veteran all seven trophies, a player past the Elder only Eikthyr's and the Elder's; the fresh player was refused the Eikthyr stand, the veteran given it; with the veteran's game owning Yagluth's stone, the fresh player's request for it was not passed on (the veteran's connection never got it) and the stone was the server's again 10 s later; data sending Bonemass's stone back empty was not taken; the fresh player at Eikthyr's stone was told; a request from the fresh player in the moment their list said past the Queen, while their game still held the empty copy, was refused; then all seven stones went out to them with the trophies; a player whose list had not arrived got no stone at all until it did, then all seven with the trophies; every trophy in place at the end; patch, transpiler and raid checks pass; no exceptions from the plugin.

Changed

  • The night spawn guard's README now says which drops count for each boss, read from the game's drop tables: for Yagluth only a Torn Spirit (or a Wisp or Demister), for the Queen only a Majestic Carapace, not their trophies; "passing the trophy around" was right only for the first four bosses.

[1.13.0] - 2026-10-09

Added

  • [NightSpawnGuard] Enabled (off by default): the spawns that come with boss progress -- Greydwarfs at night after Eikthyr; Draugr, Greydwarf Elites and Shamans and Odin after the Elder; Skeletons after Bonemass (the Menhir alt biome's too); Goblins after Yagluth; Seekers, Seeker Brood and Ticks after the Queen; Charred after Fader, read from the game's own spawn tables -- are held back in a spawn zone until every player in it has got that far themselves. Until then the zone behaves as in a world where that boss still lives (the entry's key is swapped for one no world has, for the one call, and always restored, also when the game's code throws). A player's progress is read from the raids their own client still lists for them (possibleEvents): the first main raid still listed, with the forest troll and surtling raids confirming the Elder's and Bonemass's steps, so a power taken at a stone someone else filled does not count as a kill. Worked out with a port of the game's raid check over its 1.0.16 raid data, for every progress level and every combination of other people's powers (README). Raids are not touched. If the game's raid data stops matching, the guard does nothing and says so. Tested on a 1.0.16 server at midnight with every boss's key set, players in open meadows: a newcomer with a veteran, and a newcomer who took Eikthyr's power from someone else's trophy -- all 13 entries held back; two players past Bonemass -- only Goblins, Seekers, Seeker Brood, Ticks and Charred held back, and a Greydwarf Elite spawned; a player past the Elder holding four other bosses' powers -- held back from Skeletons on; two veterans -- nothing held back; after a minute of it no spawn entry was left with the stand-in key (0 of 2602).
  • [FireControl] (off by default): MaxRadius -- a fire starts only within this many metres of the source its chain began at (a lit fireplace, or where an arrow, fireball or meteor came down; every spark and fire keeps it in its ZDO); MaxSpread -- caps every spark generation count; FireplaceIgnition -- off, no fireplace sets alight what touches its flame, so a hearth, brazier or torch cannot start a fire at all. How far every fire source reaches in the game is in the README, worked out from its code and data: what each fireplace sets alight directly (the hearth about 1.4 m, the bonfire 1.7 m around and 6.2 m up, a floor brazier the floor under it, standing torches 0.1 m; sconces, lanterns, forges, kilns and smelters never), how far a spark lands (simulated with the game's own spark code: bonfire 2.9 m, campfire 1.0 m, a fire 3.9 m on flat ground and farther from a height), and how many generations follow. Tested on a fresh copy of the live world with a lit bonfire on a 14x14 m wooden deck, a hearth 1.3 m from a wooden wall and a floor brazier on wood: with the game's rules the hearth lit the wall three times in 15 s, the brazier the floor under it, and the deck burned outwards to 8.4 m within 25 s of the first spark landing (7 floors gone after 40 s); with MaxRadius = 3 and FireplaceIgnition = false nothing was lit directly, no fire started beyond 3.0 m of its source (5 stopped, 3.1-6.1 m out) and no floor burned; 0 exceptions.

Fixed

  • Fireplaces smothered for everyone but the server ([Fixes] ServerSmoke, on). A smoke source only puffs while the local player is within 64 m; a dedicated server has none, so the server never saw smoke: a fireplace in a room full of smoke -- every smoke source counts itself blocked with smoke within 0.75 m of it (0.4 m for braziers), and a fireplace blocked for 4 s goes out until the air clears -- went out for every player and kept burning on the server, using fuel, throwing sparks and setting what touches it alight; and a fire under a roof never choked. Now a smoke source puffs on the server while any player is within the same 64 m (SmokeSpawner.Spawn, one read of Player.m_localPlayer swapped for the nearest player in range), and the game's cap of 100 puffs at a time is counted once per connected player. Tested: the server made smoke around a player (up to 230 puffs with the test's fires and fireplaces), and a fire's own smoke source in a closed room counted itself blocked once its smoke had nowhere to go, as it does on a player's computer; 0 exceptions. How often that smothers a fireplace or chokes a fire depends on the room; a fire still burns 30 s at most either way.

[1.12.1] - 2026-10-07

Changed

  • Renamed to Sarkastic.gg Dedicated Simulation: the community moved from sarkastic.eu to sarkastic.gg. The DLL is now SarkasticGG_Dedicated_Simulation.dll; delete SarkasticEU_Dedicated_Simulation.dll from BepInEx/plugins when upgrading (BepInEx would load only the newer of the two, but the old file stays behind). The plugin GUID, and so the config file MVP.Valheim_Serverside_Simulations.cfg, is unchanged, and nothing else is.

[1.12.0] - 2026-10-03

Valheim 1.0.16 (Steam build 25527701, 25 September 2026) changes none of the methods this mod patches; 1.11.1 passes the patch, transpiler and raid checks on it, and so does this release.

Added

  • [CharacterGuard] (off by default): a server-side check on the characters players join with, for servers whose players keep vanilla clients. A vanilla client never sends its inventory, so the server cannot read or replace it; it sees the character's id, what it wears (with weapon upgrade levels), and the raids it is ready for, which the game derives from the items it knows and the bosses it has beaten. A character this world has not seen must be fresh -- still ready for Eikthyr's raid and wearing nothing beyond a level 1 workbench -- or it is shown a message and kicked (NewCharacterAction: Kick, Log, Ignore). A known character that comes back wearing something new or ready for other raids than when it left is logged (ChangedAway: Log, Kick, Ignore). Characters that built something or own a bed or tombstone in the world count as known, so existing players are not locked out; admins are exempt. Console: characters, allow <name>. The list is kept in <world>.characters.txt. Tested with fake players on a copy of a live world: a fresh character and one with a club and rags let in, one with iron gear and progress kicked, a builder of the world let in; on their return a new sword and new raid readiness logged (or kicked with ChangedAway = Kick), an unchanged character let in, and allow both cancelling a pending kick and letting a kicked character in.
  • [ItemLedger] Mode (Off by default; LogOnly; On): an account per character of the items that lock progress, for servers whose players keep vanilla clients: what it got on the server and what it put back into the world. In: picked up (a DestroyZDO from that player), taken out of a container or off a stand. Out: dropped -- only what has been in a bag, which the game marks, so the resources of a removed piece do not count, and what falls out of a chest or stand its owner removed is matched against what it held -- put into a container or on a stand, fed to a smelter, cooking station or fermenter, offered at a boss altar, built with, died carrying. What a character wears and holds is checked every 10 seconds; an upgrade shows as a higher quality. More out than in, and than it could have crafted, came from another world: written to <world>.guard.log, and with On taken away -- a dropped stack cut down, a container or stand emptied of it once nobody uses it, a smelter's queue undone, a boss altar refusing -- and the player told (Message, AltarMessage). Only exact accounts are acted on: a new, fresh character from zero, any other after its first death here, when the tombstone shows the whole bag; for the first GraceHours (168) no character counts as new. Admins are exempt. Accounts in <world>.items.txt. Findings for an account that is not exact yet go to the guard log only, not to the BepInEx log.
  • Both features keep out of the game's way: every patch catches its own faults and logs them (at most 20 in a quarter of an hour), so a fault never stops the original method -- a disconnect, a boss summon, object data from a player. If the accounts or the known characters cannot be read, that feature stays off until the next start and leaves the file as it is; a save cut short is read back from its .tmp. Container contents are read without creating the items, as Inventory.Load would for every stack. The guard notes what an online character wears and knows every minute, since a server shutdown does not disconnect the players one by one.
  • [ItemLedger] Items = auto: the items that lock progress, from the game's own data -- what cannot go through a portal, what bosses drop and what summons them, every material that all recipes and pieces using it need more than a level 1 workbench for, every item that all its recipes need more for. 544 items in Valheim 1.0.16, listed by reason in the guard log at startup; ExtraItems and ExcludeItems adjust it, and what traders sell is always left out.
  • Crafting is not seen, so a tracked item counts as crafted from the tracked items the character got, reckoned in its favour wherever the game leaves room: the crafting bonus at its luckiest (three more per craft at a station, for a lucky batch of five), an upgrade made at the 1.0 upgrade station with upgrader items, an item broken there handing back 35 % of its materials (never one it wears), a caught fish bringing the most its extra drops can (a Fish10 one silver, a Fish6 two iron ore). A recipe is paid in full or not at all, and an item the game removes after an hour with nobody near is no pickup.
  • Tested on a copy of a live world on Valheim 1.0.16, with simulated vanilla clients sending through the server's own network entry points (ZDOMan.RPC_ZDOData, routed calls): black metal put into a chest, iron dropped, iron scrap and copper ore fed to a smelter, a boss trophy put on a stand, silver beyond what one caught fish brings and copper the game had removed 37 m away put into a chest, a boss summoned with blood the character never got -- logged, and with On taken out of the chest, cut, dropped from the smelter's queue, taken off the stand, refused by the altar. Bronze dropped beyond the four bars its copper and tin could make with the best luck: the one bar over logged, and only logged when the drop was picked up again at once. Nothing logged for a chest a player removed or a trophy taken off a stand, picked up again; a caught fish's silver; bronze from a sword broken at the upgrade station once it was no longer worn. Its upgrade while worn was reported as the cheaper of the two ways, one upgrader item. An older character's silver noted as unverified until its death, then 2 beyond what it took back taken away; a forge built from copper it never got only logged.
  • The character guard and the item ledger write their own log, <world>.guard.log.

Fixed

  • The console help said setkey and removekey need devcommands; they do not (only listkeys, resetkeys and the like are cheats).

[1.11.1] - 2026-09-25

An audit of every patch against Valheim 1.0.15 and 1.0.12, prompted by MistrCech/valheim-serverside#2. That pull request's premise does not hold -- the only public build, 1.0.15 (25390671, 18 September 2026), still calls IsAnyPlayerInEventArea in RandEventSystem.FixedUpdate, and raids go active on it with this mod -- but the patch was fragile in the way it describes.

Fixed

  • Random events: the RandEventSystem.FixedUpdate patch finds the three sites it needs by the field and method each uses and changes the method completely or not at all, with a warning naming what is missing. Before, if a game update moved the call it reuses, it did nothing without a word -- or reversed the local player check without replacing the local player's position, which would throw on every fixed step of an event. Checked by blanking each site in the real IL: 1.11.0 reversed the check alone in two of three cases, 1.11.1 left the method unchanged and warned in all three. The same for SpawnSystem.UpdateSpawning, whose patch threw instead and so switched off the whole mod.
  • Players still logging in (version check, password) no longer count as players standing at the world origin. Every per-player loop counted them, so while someone sat at the password prompt the server loaded the zones around the origin and simulated what is there. Measured with a player who never finishes logging in: 1.11.0 loaded the origin zone and 9 objects around it, 1.11.1 nothing.
  • Ship.UpdateOwner checks that the ship still has its network object, as vanilla does: the timer can fire in the frame the ship is removed.
  • Event spawners check the player against the active event, the one whose spawners they get, instead of the random event.
  • ValheimPlus compatibility is patched outside the per-feature safety net, so a ValheimPlus version its patches do not fit threw out of startup and left the whole mod unpatched. It is now logged and the rest applies.
  • [Fixes] DungeonLoadGuard runs after ValheimCommunityPatch's own fix for failed dungeon rooms, as its description always said; before, that depended on the plugins' load order.

Changed

  • [MaxObjectsPerFrame] MaxObjects accepts 1 to 10000 (0 or less throttled creation to about one object per frame), and its description says what a vanilla dedicated server does: 100.
  • The vanilla drift check also watches ZNetScene.OutsideActiveArea and is reviewed for 1.0.15.

Removed

  • The WearNTear.UpdateSupport prefix. It called SetupColliders when m_colliders was set and m_bounds was not, but the game only ever sets both together, so it never did anything -- on every building piece's support update.

[1.11.0] - 2026-09-23

Added

  • [Fixes] DungeonLoadGuard (on): a dungeon whose room bundle Unity refuses to load no longer wedges its zone. In the game's asset loader a refused bundle ("another AssetBundle with the same files is already loaded") is stored as null and still reported as loaded; the next step throws, the dungeon never hears back, never spawns, and its zone stays flagged as loading -- on a server seen as an endless loop over the same dungeons (ddormer/valheim-serverside#120). Only a server running this mod gets there, since a vanilla dedicated server never loads dungeons. The guard takes over the bundle Unity already holds, reports a load that really failed as failed instead of throwing, and lets a dungeon with a failed room go so its zone keeps working and it is tried again the next time it is created. Checked by opening the forest crypt room bundles behind the loader's back: 1.10.2 logged 13 refusals, threw, and spawned none of three crypts; 1.11.0 took over all 13 bundles and spawned all three.

  • Compatibility with ValheimCommunityPatch, which speeds up the object pass around one point, the server's reference position -- the world origin on a dedicated server -- while this mod simulates around every player:

    • its zone-diff unload (ZNetScene.RemoveObjects) drops everything outside the simulation distance of that point whenever the object lists look as the game filled them, which they do when no players' areas overlap. Objects around the players were then destroyed and created again on every pass: doors, beds and items that could not be used, dungeons reloading dozens of times a second until Unity refused their room bundles as already loaded, once a crash of the server itself (ddormer/valheim-serverside#119, #120). Measured with two players walking between two dungeons: 178 to 1903 dungeon loads at up to 90 a second and hundreds of refused bundles with it, 52 loads and none without it;
    • its spawn queue (ZNetScene.CreateObjectsSorted) orders new objects by distance from that point, so this mod's ordering by the nearest player never ran.

    Neither has a switch of its own; both are removed when the world starts unless [Compat] ValheimCommunityPatchUnload / ValheimCommunityPatchSpawnQueue keeps them, the rest of ValheimCommunityPatch is left alone. With the unload kept, the object lists are marked as edited on every pass so it takes the game's own unload check, its own path for mods like this one (measured: 52 loads, no errors).

Changed

  • The frame rate patch also sets the refresh rate the game assumes (1.0.14 and later), so 30 and 60 FPS no longer depend on what display modes a headless process reports. Idea from @Merl in MistrCech/valheim-serverside#1.

[1.10.2] - 2026-09-21

Fixed

  • Valheim 1.0.14 gave PresentManager.RequestTargetFrameRate a second parameter and renamed the first (the patch that let the frame rate limiter work with v-sync), so the patch that sets the server's frame rate failed and took the whole Performance feature down with it (logged as "Feature Performance failed to apply and is disabled"). From 1.0.14 on, the server therefore ran at the game's 30 FPS, without the even world-update interval, the physics catch-up limit, the zone budget and the performance report. The patch now takes every overload of that method and its first argument by position, not by name. Reported by @Merl in MistrCech/valheim-serverside#1, who found it first and also traced it to 1.0.14.

    Checked on 1.0.15 against 1.0.12 with a server simulating a base: a client's UseDoor toggles the door, a bed takes its owner, an item a client claims stays claimed, and army_eikthyr spawns its waves -- the same on both versions.

[1.10.1] - 2026-09-13

Fixed

  • Accented letters typed into the server console arrived as ?: AMP on Windows writes its own code page to the process while Unity's Mono reads standard input as UTF-8. Lines are now read as bytes; valid UTF-8 is taken as such, anything else is decoded with [Server] ConsoleInputCodePage (1250 by default; 852, 1252, 65001), and the first such line is logged with its bytes so the right page can be set.

[1.10.0] - 2026-09-13

Added

  • Console commands broadcast <text> (a message in the middle of every player's screen, the way the game announces a raid) and event <name> <player> (a raid at that player; events lists the names). The game's own event needs a local player and cannot work on a dedicated server.
  • Anything typed on the server console that is not one of ours is passed to the game's own console, which a dedicated server has but never reads, and what it prints goes to standard output for the panel. So the vanilla server commands work too: kick, ban, unban, banned, stopevent, randomevent, and after devcommands the cheat commands that need no player: skiptime, setkey, removekey, resetkeys, listkeys, env, tod, wind. Verified on a local 1.0.12 server.

Fixed

  • [Fixes] TeleportGhosts: a player who portalled or respawned stayed visible to the players near the old spot, frozen, until they next crossed a zone line. Valheim 1.0 files an object under its new sector before it stores the new position, and the check that tells each player to drop objects that left their area reads the position, so it saw the old spot and queued nothing. The check is re-run once the position is stored; a player already told has no entry left, so nothing is sent twice. Reproduced and verified with a fake peer on a local 1.0.12 server. Reported for 1.0 by ValheimCommunityPatch ("Fix Teleport Ghost Players").

Removed

  • [AdminChat]: a Valheim client runs anything typed into the chat with a leading / as a local console command and never sends it, so /give could not reach the server from a real client. MaxGiveAmount moved to [Server].

[1.9.0] - 2026-09-12

Fixed

  • [Fixes] SaveClientChanges: Valheim 1.0 saves only the world chunks it marked as changed, and a change that arrives from a player for an object the player owns marks nothing, so the new state lived only in memory until something else in that chunk changed. The chunk is now marked when such a change arrives. With this mod the server owns nearly everything near players, so the window was small: what a player just built, the ship they steer, their drops. Reported for 1.0 by ValheimCommunityPatch.

[1.8.0] - 2026-09-11

Added

  • Console replies are also written to standard output, so a panel such as AMP shows them even with BepInEx's console off.
  • Console commands give <item> <amount> <player> (drops the items in front of that player, stacked as the item allows; the name may be a unique beginning) and players, next to save and stop, for the panel the server runs in. Valheim 1.0 only lets the host use cheat commands, so spawn from the game console says "not valid in the current context" on a dedicated server even for admins.
  • [AdminChat] (off by default): admins listed in adminlist.txt can shout /give <item> [amount], /save and /help; replies go to their console.
  • The performance log now says what the slowest frame of each period was doing: players' messages, world updates, object creation, zone generation, creature logic, other game updates, saving, and how much was Unity's own work (physics, garbage collection), with the garbage collections that ran in that frame. On the live server a ~300 ms frame showed up almost every 5 minutes without a known cause.

[1.7.0] - 2026-09-11

Added

  • [Performance] ServerTargetFps (60): the game sets a dedicated server to 30 FPS, so a frame finished in 12 ms still lasts 33 ms and every reaction to a player waits for it. Measured on the live server with one player: 30 FPS at a median frame of 35 ms while the game logic took a fraction of that. 60 halves the wait whenever the server has the headroom and changes nothing under load. 0 keeps the game's 30.

[1.6.0] - 2026-09-11

Added

  • [Performance] settings that smooth the server's frame time, and a periodic performance log (StatsIntervalMinutes): FPS, frame time (average, median, 95%, 99%, worst), fixed steps per frame and the share of time their game logic takes, the cost of sending world updates and of generating zones. With this mod the server simulates everything, so its frame time is what players feel: a felled tree turns into wood only after the server has caught up.
  • SendIntervalMs (100): every player gets world updates at a fixed interval, the sends spread over the frames in between. Valheim serves one player per frame, so with N players each waited N+1 frames: at 15 FPS with 4 players about 330 ms, and longer with every player who joins. Measured under load with 4 players: about 3x as many sends for 0.4 percentage points more of the main thread.
  • MaxCatchUpMs (100): after a slow frame Unity reruns the fixed step (physics, every character, creature AI) once per 20 ms missed; Valheim allowed 200 ms of catch-up, i.e. 10 steps after one bad frame, which made the next frame bad too. Now at most 5. Game time runs slightly slow during such frames.
  • MaxZonesPerTick (1): new zones are generated one per zone tick, players taking turns, instead of one per exploring player in the same frame. Measured under load with 4 players exploring: the worst zone tick fell from 70-177 ms to 22-47 ms, with the same number of zones generated per minute. It caps how many zones a tick generates, not what one costs: a zone holding a large location can still take a few hundred ms.

None of these raise the frame rate; they cut the spikes and the waiting between server and player. Measured on a local copy of a live world with four simulated players walking outward and an artificial 50 ms of load per frame plus a 300 ms stall every 10 s.

[1.5.0] - 2026-09-10

Added

  • Console commands on standard input: save saves the world, stop saves and shuts down cleanly. Lets server panels such as AMP stop the server without losing progress since the last autosave (AMP: App.ExitMethod=String, BepInEx console disabled).
  • Cap Unity job worker threads at 8 ([Server] UnityJobWorkers). Unity starts one per CPU core and idle ones still use CPU; an idle server on a 24-thread machine went from 108% to 38% of a core.
  • The server creates the objects nearest to a player first. It sorted them by its own reference position, which on a dedicated server lies outside the world, so after a portal or entering a new area the surroundings of a player could be the last to become active. Idea from upstream PR #100 by jsza.

Changed

  • Default per-player send queue raised from 32 KB to 48 KB. On a live server with four players it was full in about 0.1% of send ticks, only in bursts (portals, new areas), peaking at the 32 KB limit. 48 KB at 20 ticks/s is about 960 KB/s, just under the Steam send rate cap. Existing config files keep their value.

Fixed

  • Generate ghost zones around players again, as vanilla does. The ZoneSystem.Update replacement only created local zones, which reach the near simulation distance, so unexplored land in the far ring was not generated ahead of players and distant objects there (large trees, cliffs, the Mistlands mist) appeared only much closer.

[1.3.0] - 2026-09-10

Added

  • Server-side networking limits from BetterNetworking (by CW-Jesse, MIT): per-player send queue 32 KB instead of 10 KB and Steam send rate 256-1024 KB/s instead of 150 KB/s, configurable under [Networking], with a periodic log of how often each player's send queue was full. Clients stay vanilla.

[1.2.0] - 2026-09-10

Changed

  • Renamed to Sarkastic.eu Dedicated Simulation, a fork of Serverside Simulations by ddormer. The plugin GUID is unchanged; delete Serverside_Simulations.dll when upgrading.
  • Game members are accessed directly instead of through Traverse, so game updates that rename them fail the build instead of silently doing nothing. If a Core patch fails to apply the mod now removes all its patches and the server runs vanilla. A startup check logs a warning when a vanilla method replaced by the mod has changed since it was last reviewed.

Fixed

  • Update for Valheim 1.0 (Vector2s zones and SimulationDistance). Based on ddormer/valheim-serverside#118 by @mreastman, which also fixed item pickup and the Pickable.RPC_Pick null reference.
  • Fix the ZoneSystem.Update replacement dropping vanilla behaviour: location prefabs loaded by the server were never released (a memory leak), zones were created while locations were still being generated, and ZoneSystem.TimeSinceStart stayed at zero.
  • Fix the server never creating objects when started with a non-classic -simulationdistance (any value other than 0 or 2).
  • Fix Valheim 1.0 null references on the server: ship sails changing (repeated every physics frame while the sail moved), taking items from a Frost Foundry (the item was duplicated and stayed in the station) and leviathans diving.
  • Update ValheimPlus compatibility (GetNearbyChests)

[1.1.9] - 2025-05-14

Fixed

  • Fix missing effects (Revert EffectList.Create patch)

[1.1.8] - 2025-04-20

Added

  • Remove AudioMan.Update, reducing future error spam

Fixed

  • Fix null references to WearNTear.m_bounds and Humanoid.m_currentAttack.m_character
  • Fix shield generators and audio log spam

[1.1.7] - 2024-11-12

Added

  • Environment.props and bepinex publicizer

Fixed

  • Update method references to static, fixing Bog Witch launch issue. Thanks to @bpage-dev

[1.1.6] - 2023-11-22

Fixed

  • Fix exception when updating ship owner in 0.217.28
  • Fix MaxObjectsPerFrame transpiler for Valheim 0.217.28

[1.1.5] - 2023-10-25

Fixed

  • Fix private method access errors in Release build

[1.1.4] - 2023-10-24

Changed

  • Mistlands update

Misc

  • Fix AssemblyPublicizer output path.
  • Update BepInEx, Harmony and MonoMod libs

[1.1.4] - 2023-10-24

Changed

  • Mistlands update

[1.1.3] - 2021-09-22

Fixed

  • Fix Valheim Plus autofuel compatibility.

[1.1.2] - 2021-09-16

Changed

  • Remove old fishing fixes (appears to have been fixed in latest Valheim patch)

[1.1.1] - 2021-05-14

Changed

  • Update to BepInEx 5.4.10

Fixed

  • Fix fishing

[1.1.0] - 2021-05-07

Added

  • Added "max objects per frame" configuration. Allowing for faster or slower area loading on the server.

[1.0.3] - 2021-04-21

Fixed

  • Fix Ship container access and improve Ship ownership transfer
  • Fix Ship taking 10 damage when owner changes to server.

[1.0.2] - 2021-04-19

Fixed

  • Fix objects not being created on the server in 0.150.3.

[1.0.1] - 2021-04-15

Fixed

  • Prevent event monsters from spawning outside of the random event area, during a random event.