Valheim
Install with App

Details

Latest version
0.8.5
Last Updated
First Uploaded
Downloads
161
Likes
0
Size
81KB
Dependants
ADDatHost Valheim hosting
30% off

Sarkastic.gg Resource Regrowth

version: 0.8.5 players: vanilla clients Valheim: 1.0 BepInEx: 5.4.2351 license: MIT

Source code · Report a bug · Changelog · Dedicated Simulation

Join us on Discord Support us on Patreon

A server-side BepInEx plugin for Valheim 1.0 dedicated servers. It brings one-time world content back once an area has been left alone for a while:

  • Surtling core stands get their core back.
  • Treasure chests (never player-built ones, and never one holding something of a player's) are restocked with fresh loot, whatever was left in them replaced.
  • One-time creature spawners (dungeons, camps, caves) spawn their creature again.
  • A location regrows as a whole: a dungeon with its entrance, a camp, a village or a ruin comes back all at once -- every core, every creature and every chest in it.
  • Terrain that players dug, raised, levelled or paved slowly returns to the land's own shape, except around player bases and wards.
  • Location objects (off by default): destroyed objects a location or village generated -- wild beehives in abandoned houses, for example -- are put back exactly where they stood. A muddy scrap pile in a sunken crypt (mudpile2, add it to Prefabs) is one too: the iron comes back with the crypt's chests and creatures, and what is left of the pile that was hit goes with it.
  • Zones (off by default): a zone players have been in is generated again by the game itself once nobody has been near it for three days -- trees, rocks and ore deposits, bushes, pickables, the location with its buildings, chests, creatures and dungeon, and the ground -- as if no player had ever been there. Never at a base (a workbench); players' things stay as they are.

Players need nothing: vanilla clients work. The plugin depends on BepInEx and nothing else. It patches one game method, and only with Zones switched on (see Zones).

Recommended with Dedicated Simulation (Sarkastic_gg/Dedicated_Simulation, GitHub), a fork of Serverside Simulations by ddormer that keeps the world simulated on the server. This plugin works on its own, but the two are made to run together: the zone reset generates a zone the way Dedicated Simulation does.

How it works

Everything handled here stays in the world when it is used up, so regrowing it only changes the object's data. The plugin deletes and creates no objects.

What Depleted when Regrown by
Surtling core stand the core was taken (picked) setting picked back to false
Treasure chest vanilla filled it once, and it is now empty or a player has been near it since it was last restocked replacing its contents with fresh loot from the chest's own loot table, as vanilla fills it when the chest is first generated
One-time spawner its creature is gone clearing the spawned link; vanilla spawns again the next time a player loads the area
Terrain (a zone's _TerrainCompiler) any vertex of the zone's heightmap is modified or painted a step every StepHours: every height change outside a protected area is divided by Divider (1.7) and dropped once below MinDelta; paint goes back to nothing once the height under it is back, except paved and cultivated ground. The data is written in the game's own format; loaded terrain reloads it and every client rebuilds the ground.

Location objects are the one exception to "no object is created": a destroyed beehive is gone from the world, so it is created again. Where it stood is known exactly, not guessed:

  • every location the world generated keeps a record of its position, rotation and seed, and every village or camp keeps the position and rotation of each of its buildings;
  • the object's place follows from the location's or building's layout, the way the game placed it;
  • objects that only spawn by chance are decided by replaying the game's own roll from that seed, so only places that really had one count.

Checked on a live world: of 169 beehive places the replay said 47 had a hive, and all 29 hives still standing were on exactly those places (same height to the centimetre), none anywhere else. A hive hangs under the roof of its house; when the pieces of the house that held it are gone (pulled down by players), the hive is not put back, so nothing floats in the air.

A pile or rock that is hit does not vanish at once: the game replaces it with its fractured remains (<name>_frac), which are what is mined. So when an object is put back, the remains of it within 1.5 m go (ClearRemains), or it would stand twice. Checked on a live world copy: a sunken crypt had 301 pile places, the replay said 174 had a pile, 162 stood whole, 12 were gone -- and there were exactly 12 fractured remains; 11 were put back at their own places (the twelfth had lost a gate that held it) and 11 remains went, none of the new piles on another. A base on the surface does not hold back what comes back inside a dungeon (5000 m up), nor does a tombstone down there hold back the surface above it.

Whether a player took everything from a chest, some of it or nothing cannot be told from the chest, so a chest counts as used once a player has been near it; when its time comes it is emptied and filled afresh, partly looted or not.

A location regrows as a whole ([Locations] Together). Every object belongs to the location the world generated it with: inside a dungeon (5000 m up) to the dungeon its generator built -- rooms lie within about 60 m of it -- and to the location at the same spot on the surface, so a burial chamber's interior and its entrance are one; a cave whose interior comes whole with the location (troll and bear caves) by its interior radius; on the surface to the location whose ground it stands on. Each used-up object keeps its own clock, and as soon as one of them has waited its category's AfterHours, everything used up in the location comes back together, and every chest in it is restocked: a burial chamber whose cores were taken 10 hours ago returns whole, chests and draugr included. With Together = false each object regrows on its own.

Every CheckIntervalMinutes the plugin scans the world. A location (or a lone object) regrows only when all of these hold:

  • one of its objects has been used up for its category's AfterHours;
  • no player has been near any of it for IdleHours;
  • none of it lies within PlayerBuildRadius of a player-built piece or tombstone;
  • none of it is in use: no connected player owns it, and the server does not have it loaded. Dedicated Simulation, for example, loads the zones around players on the server.

A scan of a large world (165,000 objects) takes about 15 ms of work, spread over about 40 frames.

Zones

Vanilla never brings back a felled tree, a mined copper or tin deposit, a stone tower or a house players pulled down. With [Zones] Enabled, a zone (64 m square) that a player has been in or next to is, once nobody has been near it (PresenceRadiusZones) for IdleHours (72), generated again by the game's own zone generation, the way Dedicated Simulation generates the zones around players (ZoneSystem.SpawnZone, ghost mode). Every object the game makes takes the place of the old one of its kind standing there, which goes; what is left over of what only the zone itself makes -- vegetation, stumps and logs, wild creatures, items lying about, the record of what was done to the ground -- goes too. So trees, rocks and ore deposits, bushes, pickables, the location with its buildings, chests, spawners and dungeon come back. Measured on a copy of a live world against a fresh world with the same seed: 860 of 916 zones without a location came out exactly as first generated, the rest with a few trees or rocks elsewhere (the game decides placement partly by what colliders are around at that moment).

A location comes back the way round it was: the game draws its rotation from an unseeded random number when it first places it, so the rotation is read from the location's own record before the zone is cleared and given back when it is placed again (a patch of ZoneSystem.SpawnLocation, the only game method this plugin touches). With the same rotation its dungeon gets the same seed and the same rooms. Because old objects are matched to what the game made object by object, a location or dungeon that reaches over the zone's edge (a boar runestone's spawners, a crypt's rooms) is replaced there too, and a neighbour's objects reaching into the zone stay.

Players' things stay exactly where they are: pieces they built (also portals, beds, crops), tombstones, ships and carts, items a player has carried (an item remembers having been in a bag), tamed creatures, and the world's chests, barrels and stands holding something of theirs (the old one stays, the game's new copy goes). Beside a piece they built or a tombstone nothing changes -- within 4 m for trees, rocks, buildings and chests, 2 m for smaller things, and wherever the colliders of a big rock, deposit or tree reach within 2 m of one, the game's new copies go and the old ones stay -- so no tree grows through a wall, nothing they kept by their walls goes, and a mountain rock (its stone covers the ground up to 23 m from its centre) does not come back over a chest built where it was mined out. A zone with their pieces in it keeps its ground record (Terrain regrowth deals with it, with its own protections).

Never generated again:

  • a zone with a workbench in it or within PlayerBuildRadius (48 m) of it: someone's base. Of players' things only a workbench keeps a zone as it is;
  • a protected location (ProtectedLocations: the start temple with the guardian stones, boss altars, traders, Hildir's dungeons) and every location the game places only once; nothing left over within a protected location's reach in a neighbouring zone goes, and no trader;
  • a zone the server has loaded or next to one it has, or anything in it a connected player owns;
  • anything in the first GraceMinutes (30) after a start: who was near while the server was off is not known.

Fields, paved roads, dirt paths, dug and raised ground and the trees players planted go with the reset unless a workbench is near: whoever wants a road or a field kept puts a workbench by it. A zone with players' pieces in it (crops are pieces) keeps its ground record, so a field with crops on it stays a field.

If the game cannot generate the zone now, or throws while doing it, nothing old has been deleted yet: whatever the attempt made is removed, the zone is marked generated and its location placed again, and after an exception zone resets stop until the next start.

At its first run every zone generated so far counts as visited -- what players did there before cannot be told -- and no zone is generated again before IdleHours from then. Presence is kept for the longer of the two idle times, also while Zones are off, and a zone without a record counts as seen at the time from which presence is known completely, so switching Zones on later or raising IdleHours never makes a recently visited zone due. At most PerPass zones per pass, longest idle first, one per frame (each a few milliseconds to a few tens); a zone that cannot be done now waits an hour. DryRun (on by default) only logs which zones would be generated again and what they hold.

Terrain protection

Ground within the radius of a player base object -- anything the game itself treats as a base when it decides where creatures may not spawn (a PlayerBase effect area: workbench, forge, stonecutter, beds, fires ...) -- or of a ward is left as it is; the radius is the object's own (ProtectMultiplier scales it). A base that is gone, say a workbench broken by creatures, protects nothing any more, and the land returns. ProtectPiecesRadius (0) adds a radius around every player-built piece and tombstone for those who want roads and fields kept whole; paved and cultivated paint is kept anyway (KeepPaved, KeepCultivated), only its height returns. The log lists the protecting kinds and their radii at start.

Time

All times are real hours (wall clock, UTC). They keep counting while nobody is online and while the server is stopped. The world clock would not work here: on a dedicated server it only runs while a player is online.

The plugin cannot know what happened before it was installed. Both clocks start at its first run on a world, so nothing regrows until IdleHours and AfterHours have passed since then.

After a pass that regrew anything the world is saved right away (the same call as the autosave), so a crash cannot lose the regrowth while keeping the reset clocks.

What it remembers is kept in <world>.regrowth.txt next to the world save. That file records when players were last near each zone, and since when each object has been depleted.

Objects are identified by prefab and position, because the game hands out new object ids on every world load.

Limitations

  • On a server where players own the objects around them (no server-side simulation), a spawner reset may not reach a player who stays connected the whole time and already had that area loaded in the same session. The game only sends a link, never its removal, so that player's game still thinks the creature exists. The creature appears once they reconnect. With Dedicated Simulation the server owns these objects, so this does not happen there.
  • A core stand or chest that some machine currently has loaded is left for a later pass.
  • Location objects: the first scan after a start reads the layouts of the locations and village buildings the world has (a few hundred prefabs, loaded in the background through the game's own loader: measured about 0.3 s of work over 45 s, no frame over 100 ms); later scans only read new ones. A location object's own zone must not be loaded, like everything else.

Install

Copy SarkasticGG_Resource_Regrowth.dll to BepInEx/plugins/ on the server and start it once. That creates BepInEx/config/sarkasticeu.resource_regrowth.cfg.

It starts in dry run: it only logs what it would regrow. Watch the log for a few passes, then set DryRun = false. The config file is re-read before every pass, so DryRun, the delays and the logging amount change without a restart; Enabled, the prefab lists and the exclusions are applied at startup.

Configuration

Section Key Default Meaning
General Enabled true
General DryRun true only log, change nothing
General CheckIntervalMinutes 10 real minutes between scans
General LogDetailsPerPass 20 objects logged individually per category and scan
Safety IdleHours 5 real hours since a player was near the zone
Safety PresenceRadiusZones 2 a player counts as near all zones (64 m) within this many of their own
Safety PlayerBuildRadius 48 metres around player-built pieces and tombstones that are never touched
Locations Together true a location regrows as a whole: once one thing in it is due, every core, creature and chest in it comes back
SurtlingCores Enabled / AfterHours true / 10
SurtlingCores Prefabs Pickable_SurtlingCoreStand hide-when-picked, non-respawning pickables to restore
TreasureChests Enabled / AfterHours true / 24 hours after a player was first near the chest (or it was seen empty) since it was last restocked
TreasureChests ExcludePrefabs chest prefabs never to refill, e.g. TreasureChest_meadows_buried
DungeonMonsters Enabled / AfterHours true / 24
DungeonMonsters ExcludePrefabs spawner prefabs never to reset
LocationObjects Enabled / AfterHours false / 48 put back destroyed objects of locations and villages (needs a restart)
LocationObjects Prefabs Beehive which ones; only world-generated objects are ever put back
Zones Enabled false generate visited zones again as the world first made them (needs a restart)
Zones DryRun true only log which zones would be generated again
Zones IdleHours 72 real hours with nobody near before a zone is generated again
Zones PerPass 8 most zones per pass
Zones GraceMinutes 30 minutes after a start before any zone is generated again
Zones RefreshNow false one-off, switches itself off in the config: in the first pass after a start, generate again every zone nobody has been near for the last 30 minutes (no IdleHours, no GraceMinutes, no PerPass) and bring the ground of every modified zone back at once, paint too; what a workbench or a ward protects stays
Zones ProtectedLocations StartTemple, boss altars, traders, Hildir's dungeons never generated again (also every location placed only once)

Build

Requires the .NET SDK and a Valheim dedicated server with BepInEx installed (for the reference assemblies). Point VALHEIM_DEDI_INSTALL at it, or pass -p:ValheimServerDir=...:

VALHEIM_DEDI_INSTALL=/path/to/valheim_server dotnet build src/ResourceRegrowth.csproj -c Release

The DLL ends up in bin/Release/.

License

MIT, see LICENSE. Copyright (c) 2026 MistrCech / Sarkastic.gg.

Thunderstore development is made possible with ads. Please consider making an exception to your adblock.