Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
ValPerfPatcher
Runtime fixes for performance hot spots in popular mods and the base game (less garbage, faster startup and world loading), found with ValPerfMonitor.
| Date uploaded | a day ago |
| Version | 1.0.6 |
| Download link | BarryWhite-ValPerfPatcher-1.0.6.zip |
| Downloads | 7 |
| Dependency string | BarryWhite-ValPerfPatcher-1.0.6 |
This mod requires the following mods to function
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.2351README
ValPerfPatcher
Runtime fixes for performance hot spots found with ValPerfMonitor. It patches other mods in memory at startup. It never edits their files, and each mod keeps working the same way, minus the wasted work.
Fixes
| Fix | Target mod | Problem | Change |
|---|---|---|---|
| Skip idle zone sorting | HearthBelow 0.6.2 | Every frame it sorts all terrain zones with a new closure, even when no zone has work (~1 KB of garbage per frame) | On frames with no queued zone work, run only the distant-terrain and visibility updates. Frames with work run the original unchanged. |
| Cache backpack check | AzuExtendedPlayerInventory 2.6.1 | Its Adventure Backpacks compatibility asks "is this a backpack?" several times per frame, and each answer allocates (~2.5 KB per frame) | Remember the answer per item type, only once in a world |
| Backpack check without garbage | Adventure Backpacks 2.2.9 | Its patches on the game's ownership check and network calls read every object's name (gameObject.name, a new string each time) to spot its hidden backpack object: about 56 bytes on each of hundreds of checks per frame |
The name is read once per object and the answer remembered |
| Minimap level text a few times per second | Creature Level & Loot Control 5.0.6 | Rebuilds the minimap's area level text every frame (a new dictionary, text replace, number text and a copy of the level thresholds, about 0.6 KB per frame) | Rebuilt at most every 0.25 s (setting Minimap level refresh seconds); the first update after the minimap opens runs at once. The shown level can lag by up to that long. |
| Name trader pins only when the map updates them | BetterMap 1.1.0 | To name the trader pins it looks up every location on the map on every frame (~0.1 ms and ~0.8 KB per frame), although the game only adds or removes location pins every 5 seconds | Runs only on the frames where the game updated those pins. Same pins, same names. |
| Other players on top | Valheim itself (and TargetPortal 1.2.7) | The map draws icons in the order they were made, so another player's icon is often hidden under pins made after they joined (locations, mod pins, pins scrolling into view) | Other players' icons are moved to the top of the big map and minimap whenever the pins update (only when the order is wrong). While TargetPortal's portal map is open, its portal icons stay above the players. Not a performance fix; switch it off with Map: other players on top. |
| Reuse creature spawn entries | Mods bundling CreatureManager (Bestiary 1.2.1, Air Animals, Land Animals) | Every time an area loads, the mod takes its creatures out of the shared spawn list and builds them again from its config, with reflection per creature (about 0.35 ms per area with Bestiary) | Built once, then reused while they are still in the list and none of that mod's settings has changed. A setting change (also one synced from a server) rebuilds them at the next area, as before. |
| No stacked copies in new areas | Mod vegetation (RtD Ocean 2.2.51) | Some mod vegetation asks for groups of plants with no spread, so the extra plants of a group land exactly on the first: invisible copies that cost as much as real ones and stay in the world. RtD Ocean's seaweed, sponges, coral stones and crops do this (about a third of its seaweed near the coast) | In newly generated areas, one plant per group. The game's own vegetation and areas already generated are not changed. |
| Reuse gamepad button names | Jotunn 2.30.2 | "Joy!" + name builds a new string on about 80 input checks per frame (~2.4 KB per frame) |
One cached string per button |
| Reuse dig preview fit | HearthBelow 0.6.2 | While a digging tool is held, the dig preview fits its rings to the ground every frame: up to 240 raycasts (about 0.2 ms per frame) | While the aim stays put (within 1 cm), reuse the last fit for up to 0.2 s. The preview and its settings are unchanged. |
| Reuse permission section name | Server Devcommands 1.115 | Every permission check (several per frame) lowercases the same section name into a new string | Lowercase it once and reuse it |
| Start building the world early | Riverheim 1.2.1 | Riverheim builds its whole world map (about 7 s on 32 threads) only after the game's 1.5 s start pause and the scene load | The same build starts in the background the moment a world is started from the menu; Riverheim takes its results when it gets there. Only when hosting your own world. |
| Biome map on all CPU cores | Valheim itself, with Riverheim 1.2.1 | Every world load the game looks up the biome and height of 4 million map points one at a time (about 5 s with Riverheim) | The same lookups split across CPU cores; each point is still filled once, so the map is the same. Only on Riverheim worlds, and only while no other mod hooks those lookups. |
| Save its settings once | Comfort Tweaks 3.3.11 | On the first world load it adds a setting for every comfort piece and fire (about 260), and BepInEx rewrites the whole 52 KB config file after each one (about 1.5 s) | Saves are held while it sets them up, then the file is written once. Same file. |
| Throttle static object ground checks | Valheim itself | Every rock, stump and similar object casts a ray to check for ground under it about every 0.3 s, forever (about 85 per frame) | After a check that leaves the object where it is, check again after about 2 s (setting Static object ground check interval, 0 = game default). An object can take up to that long to start falling after the ground under it is dug away. Works the same with Valheim Community Patch. |
| Distance check without garbage | Valheim itself | The "is this near the player?" check builds a throwaway 3-number array on every call (about 50 bytes, Mathf.Max with three values). The object ownership pass runs it for every nearby object every 2 seconds (~640 KB each time), and building pieces, rocks, spawners and object loading use it too |
Same result computed without the array. Measured: that pass went from 643 KB to under 5 KB, about one garbage collection per minute fewer standing still. Valheim Community Patch 0.35 and later has the same fix; with it installed this one stands down. |
| Read the translation files once | Valheim itself | Mods make their own English translation objects, and each one re-reads all 13 of the game's translation files (every language column, character by character): about 0.4 s each time, 9 s at startup with 62 mods | The words each file gives per language are kept after the first read and added instead. Mods' own translation hooks still run, so the words are the same. Starts before every other mod (a second small plugin, "ValPerfPatcher (early start)"), so the loads made while mods start are covered. |
| Keep a fast-loading copy of mod asset bundles | Any mod with LZMA asset bundles (RtD Ocean, More World Locations, Bestiary, Valheim Armory, OdinShip and others) | Unity unpacks an LZMA bundle completely every time it loads it (about 8 s at every startup with those five; More World Locations' location files whenever they load) | The first load converts a copy to LZ4 in the background (BepInEx\cache\ValPerfPatcher); later loads use the copy. 24.8 s -> 15.7 s loading mods. Works whichever way a mod loads its bundles. Same assets; a mod update, or a game update that changes Unity, gets a new copy; copies unused for 30 days are deleted. |
| Reuse loaded mod translations | Mods bundling LocalizationManager (AAABuildMenu, Bestiary, GapFiller, Seasonality, HearthBelow, Comfort Tweaks, Sailing, the Azu mods and others) | Every time any translation object is set up, each copy searches the whole BepInEx folder and re-reads its translation files; some do it once per game translation file | After its first load per language, each copy adds its already loaded words. |
Measured for the HearthBelow zone sorting, AzuExtendedPlayerInventory and Jotunn fixes (ValPerfMonitor, same spot, with and without): the garbage those three mods create dropped from 5.4 to 1.2 KB per frame, and garbage collections from 12 to 9 per minute. The gain is less garbage collection (fewer small stutters), not CPU time: the HearthBelow fix's CPU saving was within run-to-run noise.
Measured for the in-game fixes (62 mods, standing still in the Meadows, same spot, all fixes on vs all off):
| Off | On | |
|---|---|---|
| Garbage created by mods, per frame | 12.2 KB | 5.4 KB |
| Garbage collections per minute | 9 | 7 |
| CPU time in mods, per frame | 0.71 ms | 0.55-0.62 ms |
Startup and world loading (same 62 mods): 54.1 s to the main menu with the translation fixes off, 41.1 s with them on. World loading on a Riverheim world: 23.3 s -> 14.4 s until the player spawns.
Safety
Every fix:
- only applies when its target mod is installed (the base-game fix always applies);
- checks that the target code still looks the way it was analysed, and stands down with a log warning if a mod update changed it;
- keeps the target mod's behaviour the same;
- can be turned off on its own in
BepInEx/config/valheimmods.ValPerfPatcher.cfg.
The startup log says which fixes were applied, for example HearthBelow: skip idle zone sorting: applied.
Requirements
- Valheim 1.0 with BepInExPack_Valheim 5.4.23 or newer.
- Only needed where you want the speed-up. Servers and other players don't need it, and it sends nothing over the network. None of the target mods is required: fixes for mods that aren't installed do nothing.
Installation
Gale / r2modman: Import → local mod and pick the release zip. Manual: copy ValPerfPatcher.dll to BepInEx/plugins/ValPerfPatcher/.
Checking that it helps
Install ValPerfMonitor in a test profile and run its full suite (Shift+F11) twice at the same spot: once with this mod's fixes on, once with them all set to false in the config. Compare the runs in BepInEx/ValPerfMonitor/compare.html (Allocations and GC per minute show the difference).
Build from source
Needs the .NET SDK, Valheim, and a BepInEx profile that has HearthBelow installed (the HearthBelow fix compiles against its DLL; at runtime it's only touched when HearthBelow is present).
dotnet build -c Release
dotnet build -c Release -p:Deploy=true
python package.py
- The first builds
bin/Release/net48/ValPerfPatcher.dll. Deploy=truealso copies it into the profile'sBepInEx/plugins/ValPerfPatcher/.package.py(Python 3) makes the Thunderstore-format zip indist/.
Paths default to a Steam install and a Gale profile named "Default". Override them: dotnet build -c Release -p:ValheimDir="D:\Games\Valheim" -p:GaleProfile="C:\path\to\profile" (the folder that contains BepInEx). HearthBelow is expected at BepInEx/plugins/Azumatt-HearthBelow/HearthBelow.dll. It targets .NET Framework 4.8 because HearthBelow does.
Adding a fix
Each fix is one file in src/ with an Apply(Harmony) method that returns null when applied or a reason when it stood down, registered in Plugin.cs with its own config switch. Before patching, verify every member it touches exists with the expected type; if not, return a reason instead of patching.
License
MIT, see LICENSE.
CHANGELOG
Changelog
1.0.9
- README: the fixes are now in two tables, one for Valheim, Jotunn, BepInEx and code shared by many mods, one for single mods, each sorted by size of gain. No code changes since 1.0.8.
1.0.8
- Minor README fixes. No code changes since 1.0.7.
1.0.7
- Other players on top: no longer creates a little garbage on every map pin update while TargetPortal is installed.
- Other players on top: if it ever hits an error, it switches itself off with one warning in the log instead of interrupting the map's pin updates.
1.0.6
- New fix: other players' icons stay on top of the map. The map draws icons in the order they were made, so a player's icon was often hidden under pins made later (locations, mod pins, pins scrolling into view). Now other players are moved to the top on the big map and the minimap. While TargetPortal's portal map is open, its portal icons stay above the players so they can still be clicked. Switch:
Map: other players on top.
1.0.5
- New fix: faster startup (and faster loading of some mod content in the world). Many mods ship LZMA-compressed asset bundles that Unity unpacks completely every time they load. Now, for any mod, the first load converts a copy to LZ4 in the background (in
BepInEx\cache\ValPerfPatcher), and later loads use that copy. With RtD Ocean, More World Locations, Bestiary, Valheim Armory and OdinShip: loading mods 24.8 s -> 15.7 s at startup. More World Locations' ~180 location files are covered too. The first launch after installing (or after a mod updates, or a game update that changes Unity) is as slow as before; new copies are made automatically. Same assets; copies unused for 30 days are deleted. Switch:Mod asset bundles: keep a fast-loading copy.
1.0.4
- The changelog is now included in the package, so Thunderstore and Hexium show it. No code changes since 1.0.3.
1.0.3
- New fix for BetterMap: it named the trader pins by looking up every location on the map on every frame; now only when the game updates those pins (every 5 s). BetterMap went from 0.14 to 0.03 ms and from about 1 KB to 0.3 KB of garbage per frame.
1.0.2
- New icon. No code changes.
1.0.1
- Works alongside Valheim Community Patch 0.35 and later: VCP now has its own fix for the game's distance check, so this mod's version stands down when VCP is installed (one info line in the log). Without VCP nothing changes.
1.0.0
First public release. Each fix has its own config switch and a check that stands it down if the target code changed.
- Less garbage while playing: HearthBelow's idle zone sorting, AzuExtendedPlayerInventory's backpack check, Adventure Backpacks' ownership check, Creature Level & Loot Control's minimap level text, Jotunn's gamepad button names, Server Devcommands' permission checks, and the game's own distance check. Measured with 62 mods standing still: garbage from mods 12.2 -> 5.4 KB per frame, garbage collections 9 -> 7 per minute.
- Less work while playing: HearthBelow's dig preview reuses its ground fit while the aim stays put; rocks, stumps and similar objects resting on the ground check for ground every ~2 s instead of every ~0.3 s (setting
Static object ground check interval). - Faster startup: the game's translation files are read once per language, and mods that bundle LocalizationManager reuse their loaded text. Starts before every other mod, so the loads made while mods start are covered. 54.1 s -> 41.1 s to the main menu with 62 mods.
- Faster world loading: Riverheim starts building its world map as soon as a world is started; the game's biome map check runs on all CPU cores on Riverheim worlds; Comfort Tweaks writes its config file once instead of once per piece. 23.3 s -> 14.4 s until the player spawns.
- Areas: mods that bundle CreatureManager reuse their creature spawn entries instead of rebuilding them on every area load; mod vegetation with no spread no longer puts invisible stacked copies on the same spot in newly generated areas.