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. Also keeps other players' icons on top of the map.
| Date uploaded | 3 weeks ago |
| Version | 1.0.13 |
| Download link | BarryWhite-ValPerfPatcher-1.0.13.zip |
| Downloads | 128 |
| Dependency string | BarryWhite-ValPerfPatcher-1.0.13 |
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. Three fixes are not speed-ups: other players' icons are drawn on top of other map pins, the water keeps moving after Seasonality freezes or thaws it, and BetterMap stops throwing errors after a relog.
Fixes
Each table is sorted by size of gain, biggest first.
Valheim, Jotunn, BepInEx and code shared by many mods
| Fix | Applies to | Problem | Change |
|---|---|---|---|
| Write each mod config file once at startup | BepInEx (any mod adding many settings: RtD Ocean, Creature Level & Loot Control, AzuExtendedPlayerInventory and others) | BepInEx rewrites a mod's whole config file after every new setting it adds; mods adding hundreds while they start wrote their files thousands of times (RtD Ocean: 8.5 s for its 127 KB file) | Until the main menu has started, those saves are held and each file is written once (4,774 saves became 59 in a 70-mod profile). Same files. Mods loading 26.3 s -> 13.3 s, launch to main menu 44 s -> 28 s. |
| 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. |
| 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. |
| 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. |
| Free unused path-finding tiles sooner near the link limit | Valheim itself | Path-finding joins its area tiles with links (about 90 per tile) and Unity allows only 65,535. Travelling fast past coasts full of creatures (sailing, portals) kept enough tiles loaded to reach that (50,000 links in 542 tiles measured), after which every new link fails and the game logs an error each time (44,000 errors in one sailing trip) | About once a second the number of links is checked; once it is above 45,000, and until it is back under 40,000, tiles nobody has used for 10 s are dropped instead of after the game's 60 s. Tiles in use are never dropped; otherwise nothing changes. Steps aside for Valheim Community Patch 0.37+, which has the same fix. |
| 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 |
| 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. |
| 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. |
| 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. |
| Remove stacked mod plants (off by default) | Mod vegetation (RtD Ocean 2.3.1) | Areas generated before the fix above, or on a server without it, keep their stacked copies for good (about 2,000 loaded at one busy base) | When switched on (World: remove stacked mod plants), the extra copy on a spot is deleted from the world once your game owns that area (the zones around you, up to 50 a second); one plant stays. Changes the world, also on a server. Never the game's own plants, pickable plants or anything built. |
| 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. |
| 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 (works alongside BossGatedPortals, which does the same for portal icons). Not a performance fix; switch it off with Map: other players on top. If it ever hits an error it switches itself off with one log warning. |
Single mods
| Fix | Target mod | Problem | Change |
|---|---|---|---|
| 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. |
| Count smelters without searching everything | AutomaticFuel 1.5.5 (fixed in 1.5.7) | Every time a smelter, kiln or furnace is created (entering a base, loading an area, the hammer's placement preview of one) it searches every object Unity has loaded, assets included, to count the smelters (about 35 ms each time with a big mod list) | One full search per world, then the list is kept up to date as smelters are created and removed. Same count. Measured: 35 ms -> 0.015 ms per smelter. |
| Water keeps moving after a season change | Seasonality 3.8.4 with Valheim Community Patch 0.36.1 (fixed in 0.37) | Valheim Community Patch remembers each water area's surface look and only animates that. When Seasonality freezes or thaws the water ("Water Freezes" on) it swaps that look, so the water you see stops moving while the waves go on: boats and fish seem to float above or sink into it, with steps between water areas, until you relog | Right after Seasonality swaps a water area's look, Valheim Community Patch picks up the new one, as the game itself would. Not a performance fix. |
| Forget map boats when leaving a world | BetterMap 1.1.0 | It keeps the boats and carts shown on the map in a list that survives logging out. The game renumbers its objects for every world session, so after logging out and back in without restarting it fails to find an old boat and logs an error on every frame (11,000 errors in 3 minutes) | The list is emptied when a world starts; BetterMap adds the boats again on its next update. |
| Helpers added once after a relog | Seasonality 3.8.4 | On every world load it adds another fall-colour helper to each tree prefab and another frozen-water helper to the area template, and those live for the whole game session: after each logout and login every tree repeats the same colour work once more and every water area gets one more collider and material copy (about 12,700 more objects per relog) | Added only when the prefab has none yet, as on the first world load. |
| Item data added once after a relog | Creature Level & Loot Control 5.0.7 | On every world load it adds another original-item-values record to every item (about 12,500 more objects per relog); it only ever reads the first one | Added only when the item has none yet, as on the first world load. |
| Forget old inventories when a world starts | MultiUserChest 0.6.2 | It keeps a slot-block record for every inventory it has seen and never removes one, so the chests and player inventories of every earlier world session stay in memory | The list is emptied when a world starts; the mod makes a fresh record whenever it needs one. |
| Forget unloaded containers | Quick Stack - Store - Sort - Trash - Restock 1.4.16 | It keeps every container it has seen in a list and only removes one when it is broken, so containers in areas you left, and in earlier world sessions, stay for good and every quick stack walks past them (184 entries for 46 real containers after 3 relogs) | Unloaded containers are removed just before each search. Same containers found. |
| 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. |
| 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 |
| Item backpack check without garbage | Adventure Backpacks 2.3.4 | Its "is this item a backpack?" check, asked for every equipped item thousands of times a minute, compares the item's prefab name with each backpack type through a new lambda each time, and "is a backpack equipped?" also builds a new list of equipped items (about 3 KB per check without a backpack on, ~2 KB per frame at a busy base) | Remember the answer per item type (an item carrying its own backpack data still counts), and look through the equipped items without the new list. Same answers. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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 |
| 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 |
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; the fast-loading asset bundle copies then cut loading mods from 24.8 s to 15.7 s. 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 fixes always apply);
- 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;
- steps aside when another installed mod already does the same job as well or better (the log says which), and works as before without it;
- keeps the target mod's behaviour the same, apart from the short delays the table mentions (ground checks, minimap level text), path-finding tiles going sooner while the link limit is close,
Map: other players on top, which only changes which map icon is drawn on top, and the two bug fixes (water after a season change, BetterMap boats after a relog), which remove a visual glitch and an error; - 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 on the PCs where you want the speed-up (or the map change). 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.27
- New fix: mods with the same favouriting code (Quick Stack, AzuAutoStore, AzuExtendedPlayerInventory and others) share one set of favourites, so a star or unstar shows in all of them at once.
- Config save fix also holds saves while a world loads, for any mod (about 450 saves written as 5-7 files); replaces the Comfort Tweaks-only fix.
- Config save fix stops holding when the game connects to a server, so server settings never reach your files.
- Config save fix ends its startup hold when a world starts loading at the latest (dedicated servers), and writes a held file first when a mod re-reads it.
- Biome map on all CPU cores now works on every world, not only Riverheim worlds (normal world: 2.8 s -> 0.95 s per load).
- Inventory border fix works for every mod that draws on inventory slots, not only AzuAutoStore and Recycle_N_Reclaim (one setting instead of two).
1.0.26
- New fix: AzuAutoStore favourite borders and Recycle_N_Reclaim trash borders no longer make garbage every frame while the inventory is open.
1.0.25
- Character save fix: its map copies and buffers are made once when a world loads and reused, so a save asks for almost no new memory (a burst of new memory at the save made the garbage collector stop the game for 120-150 ms).
1.0.24
- Character save fix: checking the packed map no longer makes about 50 MB of garbage per save (now about 1 MB), which set off a 100-170 ms garbage collection during the save.
- Character save fix: steps aside if the game stops using GZip for map data.
1.0.23
- New fix: Creature Level & Loot Control keeps its three map copies (192 MB) only when its level circles are shown.
- New fix: AzuExtendedPlayerInventory refreshes its character preview only for your own gear, not every creature's.
- New fix: AzuExtendedPlayerInventory's vanity item lists no longer grow without end.
- MultiUserChest fix now also drops unused chest records while playing, not only when a world starts.
1.0.22
- New fix: Creature Level & Loot Control asks the server for the time at most every 10 seconds instead of up to 100 times a second.
1.0.21
- New fix: floating crates, logs and dropped items are sent to the server far less often (one player's upload near floating crates: 198 KB/s down to 34 KB/s).
- New fix: creatures' walk and turn speeds are sent less often.
1.0.20
- General snow accumulation fixes, configurable.
1.0.19
- New fix: Longship Upgrades ship check without garbage.
1.0.18
- New fix: fish no longer hang in the air over the water in multiplayer.
- New fix: AutomaticFuel's torch switch key works for every player, not only the fire's owner.
- Checked against AutomaticFuel 1.6.0, Valheim Community Patch 0.38.0 and other updates.
1.0.17
- Riverheim early world build now also works when joining a server (world build on the loading screen 6.8 s -> 0.6 s).
- Checked on a dedicated server: all fixes load there.
- AutomaticFuel 1.5.8: no more "has changed" warning (the fix still steps aside correctly).
1.0.16
- New fix: character saves no longer freeze the game while the map is packed (~135 ms -> ~30 ms).
1.0.15
- Checked against Seasonality 3.8.5, Adventure Backpacks 2.4.6 and other updates.
- Adventure Backpacks 2.4.6: part of the backpack check steps aside (the mod does it itself now).
1.0.14
- New fix: no log flood when standing still on ice.
- New fix: Farming no longer logs an error for every seed planted.
- One fix failing to set up no longer skips the fixes after it.
- Seasonality ice extras: fish stay under the ice, icebergs stay put, creatures don't slide, season changes finish.
1.0.13
- Fixes step aside when another mod already does the job (Valheim Community Patch 0.37, AutomaticFuel 1.5.7).
1.0.12
- New fix: path-finding no longer runs out of links when travelling fast past busy coasts.
- Memory no longer grows per relog from Seasonality, Creature Level & Loot Control, MultiUserChest and Quick Stack.
1.0.11
- New fix: AutomaticFuel no longer searches every object when a smelter appears (35 ms -> 0.015 ms).
- New fix: water keeps moving after a Seasonality season change with Valheim Community Patch.
- New fix: no BetterMap boat errors after a relog.
- Fixed warnings about ValPerfPatcher when HearthBelow isn't installed.
1.0.10
- Faster startup of the patcher itself.
- New fix: mod config files written once at startup (launch to main menu 44 s -> 28 s).
- New option (off by default): remove stacked mod plants in already-generated areas.
- New fix: Adventure Backpacks backpack check without garbage.
1.0.9
- README: fixes split into two tables.
1.0.8
- README fixes.
1.0.7
- Other players on top: less garbage with TargetPortal; switches itself off on an error.
1.0.6
- New fix: other players' icons stay on top of the map.
1.0.5
- New fix: fast-loading copies of mod asset bundles (loading mods 24.8 s -> 15.7 s).
1.0.4
- Changelog included in the package.
1.0.3
- New fix: BetterMap trader pins named only when they update.
1.0.2
- New icon.
1.0.1
- Steps aside for Valheim Community Patch's own distance-check fix.
1.0.0
First public release: less garbage and work while playing, faster startup (54 s -> 41 s), faster Riverheim world loading (23 s -> 14 s).