
Valheim10Compatibility
Lets pre-1.0 mods load and run on Valheim 1.0 without recompiling: sector API, console commands, renamed hooks, PieceTable lists, by-name Harmony patches steered past 1.0's overloads, a Hoverable default, and a shield for item-name collisions.⚒️ Valheim 1.0 Compatibility
The Tenth World turned over, and half the realm's mods forgot how to speak to it. These bridges teach them the new tongue.
Mods built before Valheim 1.0 load and run on it — without being recompiled.
A BepInEx preloader patcher (from the MigrationStation project). Before a single line of game code runs, it rewrites Valheim's assemblies in memory and puts back the method shapes 1.0 took away, steers Harmony's by-name lookups past 1.0's new overloads, and gives the one interface that grew a method a default body. A small companion plugin keeps an item-name collision with new 1.0 content from aborting startup.
It is not a gameplay mod and changes nothing you can see. It exists for the week a major Valheim release lands and the mod list you depend on has not caught up yet. 🛡️
The latest Valheim hotfix removed three methods. Old mods can call them again.
| The old call | Now |
|---|---|
CookingStation.SetSlot — the pre-1.0 four-argument shape, and the five-argument shape of the earlier 1.0 hotfixes |
both forward to the new native, so Valheim Cuisine's cooking-station patch resolves again |
PlayerProfile.GetStat(PlayerStatType) |
forwards with the same difficulty bucket the removed method read |
ItemDrop.ResetVariantIfInvalid() |
forwards to its replacement, EnsureValid() |
🔹 Every other bridge re-checked against the hotfix — all still find their targets · 🔹 Two removals left alone on purpose: Ship.UpdateSail (the new argument is state only the caller knows) and CharacterDrop.s_pseudoCounter (its type changed) · 🔹 Re-measured: one more plugin in the 74-plugin test set now comes up fully clean.
| If… | Then… | |
|---|---|---|
| 🧭 | your mods already have 1.0 builds | you do not need this. Install it when admin tools and utility mods break on 1.0 and their authors have not caught up; remove it when they have. A fix in the mod itself always beats a bridge. |
| 📦 | you unpack it by hand | Valheim10Compatibility.Patcher.dll goes in BepInEx/patchers/ and Valheim10Compatibility.Adapter.dll in BepInEx/plugins/. Never flatten them together — in the wrong folder each one silently does nothing. |
| 🧰 | your BepInExPack is older than 5.4.2350 | update it first. 1.0 runs on Unity 6, and older packs die when Unity's log callbacks are stripped. |
| 🛡️ | your server runs AzuAntiCheat | when the Adapter DLL changes, replace the whitelist folder with the new package — never add it beside an older one, or every non-admin client is locked out. Never list the patcher DLL. |
| 🪓 | a mod's item name collides with new 1.0 content | the game now starts, but that mod is still not right for 1.0 — read its warning line in the log and ask its author for a 1.0 build. |
Over fifty methods 1.0 took away, back in the shape old mods expect.
| 🧭 The sector migration | 1.0 moved zones from Vector2i to Vector2s. GetZonePos, PokeLocalZone, IsZoneGenerated, IsZoneLoaded and HaveInstanceInSector convert for you |
| 🌍 The sector sweep | ZDOMan.FindSectorObjects, FindObjects and FindDistantObjects — what mods call to find what exists near a point |
🧮 ZDOMan.SectorToIndex |
the old arithmetic and the old -1 answer — this is what brings back resetdungeon |
| 🖥️ Console commands | both Terminal.ConsoleCommand and ConsoleEventArgs constructors — every command every admin mod registers goes through them |
| 🔀 Reordered arguments | ZNetScene.InActiveArea: the first argument used to be a zone and is now a world point — converted, not swapped |
| ➕ Appended parameters | the most common breakage and the quietest: Character.Message, EffectList.Create, four Inventory.AddItems and some thirty more |
🧱 PieceTable.m_availablePieces |
the per-category piece lists fifteen build-piece mods read, aliased to the same list object |
🎭 Hoverable.GetHoverOffset() |
a default body, so an old class that never wrote it stops failing TypeLoadException: VTable setup — and stops taking its whole plugin's patches down |
📡 ZRoutedRpc.Everybody |
a plain static field again, so ServerSync-based mods stop throwing MissingFieldException |
| 🕯️ Three patch anchors | Container.RPC_OpenRespons, RPC_TakeAllRespons and Minimap.OnMapRightClick — the name comes back so the plugin loads |
📖 Read the saga — every bridge, and the traps inside each
Every bridge is tagged [Obsolete("Valheim10Compatibility bridge -> Target(...)")], so other tools can tell a bridge from a native method by reflection, and the Harmony hook can find the native method a typed lookup belongs on. They go into assembly_valheim.dll and assembly_utils.dll.
The sector sweep — the one that matters most. AwayFromHome, Let It Grow, TortalPortal, HearthBelow and Wonderland all call ZDOMan.FindSectorObjects. 1.0 collapsed its two int distance parameters into a SimulationDistance struct and deleted ZoneSystem.m_activeArea. The bridge rebuilds the struct with classic: true — and that flag is not optional: without it vanilla filters each ring through ZonesWithinRadius and the swept area becomes a disc instead of the square ring every pre-1.0 caller is written against. FindObjects and FindDistantObjects gained a visit-tracking HashSet; the bridges pass the game's own set and clear it first, exactly as vanilla does — miss that and the second call in a session returns nothing at all.
ZDOMan.SectorToIndex was once written off as unbridgeable. Re-checked: ZDOMan is still built 512 sectors wide and ZoneSystem.SectorToIndex computes (y + 256) * 512 + (x + 256) — byte for byte the old arithmetic. Only the out-of-range answer differs: the old contract returned -1, the new one 0, which is also a legal sector. The bridge keeps -1, so callers that range-check keep working.
The console-command surface. 1.0 inserted bool hideBehindDevCommands into Terminal.ConsoleCommand's constructor and gave ConsoleEventArgs a third parameter. Breaking those two takes whole admin toolkits down at once.
Reordered, not just retyped. ZNetScene.InActiveArea went from (Vector2i zone, Vector3 refPoint) to static (Vector3 point, Vector2s centerZone). Swapping the arguments would compile, run, and answer confidently wrong. The bridges convert properly, through ZoneSystem.GetZonePos.
The appended-parameter drift. C# bakes default argument values in at the call site, so a method that merely gained an optional parameter still breaks every plugin compiled before it:
Character.Message (43 of 87 plugins) · MessageHud.ShowMessage · SEMan.AddStatusEffect ×2 · EffectList.Create (11 plugins) · four Inventory.AddItem overloads · Inventory.Changed · ItemDrop.SaveToZDO / LoadFromZDO / OnCreateNew ×2 · ItemData.GetTooltip · Heightmap.Poke · TerrainComp.Save · WorldGenerator.GetBiomeHeight · YesNoPopup..ctor · Piece.SetCreator · Player.PlacePiece · ZoneSystem.SpawnLocation · VisEquipment.AttachArmor · Humanoid.IsTeleportable · ZInput.mousePosition · Inventory.IsTeleportable() · CensorShittyWords.FilterUGC · Game.SavePlayerProfile · CookingStation.SetSlot / GetSlot (1.0's new out bool cheated is discarded into a local) · SlowUpdate.SUpdate(float, Vector2i) (callvirt, so Plant and StaticPhysics overrides win) · PlayerProfile.GetStat · ItemDrop.ResetVariantIfInvalid
PlayerProfile.GetCharacterFolderPath(FileSource) moved to SaveSystem, and its argument changed numbering — FileSource went from 0/1/2/3 to flags 1/2/4/8 — so the bridge computes 1 << old before forwarding, rather than answering the Cloud path for a Local request.
The PieceTable.m_availablePieces alias. 1.0 renamed the per-category piece lists to a private m_availablePiecesByCategory and reused the old name for a flat HashSet<Piece>. Fifteen build-piece mods read the old field — to count tabs, index them, or add a list for a custom category. The alias is a second field with the old name and type, assigned in the constructor to the same list object, so the two views cannot diverge. IL binds fields on name and type, so vanilla and 1.0-built plugins keep the HashSet and pre-1.0 plugins get the lists. A guard scan refuses the alias outright if any installed plugin passes the literal "m_availablePieces" to Type.GetField or Traverse.Field.
⚠️ The tab caveat. 1.0 inserted
PieceCategory.DeepNorth = 5, pushing Feasts, Food and Meads to 6, 7 and 8. A pre-1.0 mod bakes the old numbers in, so a piece it files under the old Feasts lands in the Deep North tab, and a custom tab created at the oldMaxshares the Meads list. Wrong tab, not a crash. If your build menu looks wrong, turn the alias off (Settings).
The default interface method. Hoverable is an interface, and 1.0 added float GetHoverOffset() to it. The runtime needs every interface member filled before a type can be built, so an old class implementing Hoverable fails the instant its assembly is scanned — not when the method is called — and every Harmony patch in that assembly goes down with it. Every vanilla call site already reads it as component?.GetHoverOffset() ?? 0f, so the patcher gives the interface method itself the body return 0f; — the same feature C# 8 formalised, applied at the IL level. A class that implements it (every 1.0-built one) keeps its own. Tested live with a controlled A/B on a 91-plugin dedicated server list: it clears every VTable failure.
ZRoutedRpc.Everybody. 1.0 made it a const. Mods compiled against the older declaration — notably the ServerSync inside AzuAntiCheat and SeedBed — read it with ldsfld, which throws MissingFieldException on a literal field and crashes the plugin at startup. The patcher makes it a normal static field again; 1.0-built code inlines the constant and never notices.
The three patch anchors — read this one. Container.RPC_OpenRespons, Container.RPC_TakeAllRespons and Minimap.OnMapRightClick were renamed or replaced by a lambda in 1.0, and plugins patch all three by name, so their absence kills those plugins at PatchAll(). These bridges restore the name so the plugin loads; they do not restore the behaviour — vanilla calls the renamed method, so a prefix on an anchor never fires. The plugin works; that one feature is inert. It is the only place where a name comes back without its meaning, a deliberate trade of one dead feature against one dead plugin, and each anchor says so in its own log line. 🕯️
A bridge is only worth injecting if it leaves the plugin better off.
| 🔍 The ambiguity guard | before injecting anything, it reads every [HarmonyPatch] in your real BepInEx/plugins folder and records which game methods are patched by name |
| 🪝 The Harmony resolution hook | a by-name patch never lands on a bridge; between two native overloads, the pre-1.0 one wins — so bridges and by-name patches live side by side |
| 🔁 The return-type rule | it refuses any bridge that would differ from an existing method by return type alone |
| 🚫 It will not guess | if a bridge cannot carry the old meaning across, it is not injected. One unresolved call kills one method; a wrong answer corrupts a world quietly at three in the morning |
| 🧾 It will not lie | every bridge reports one outcome in the log: Injected, present natively, SKIPPED or BLOCKED |
📖 Read the saga — the trap every naive "add the old overload back" patcher falls into
When a plugin writes [HarmonyPatch(typeof(Inventory), "Changed")] with no argument types, Harmony resolves that target by name alone. Add a second method with that name — exactly what a bridge does — and the lookup becomes ambiguous. HarmonyX throws out of PatchAll(), and the entire plugin dies at load, every patch in it. That is far worse than the one broken call the bridge was meant to fix — and an earlier release of this very patcher broke ProceduralRoads and Venture LocationReset exactly that way. 🩹
The guard blocks any bridge that would collide with a by-name patch. That protection was right, and it was also a ceiling: EffectList.Create was wanted by 11 plugins and stayed blocked because Infinity Hammer patches it by name. And vanilla itself creates the same ambiguity with no bridge in sight: 1.0 gave Inventory.Load, PieceTable.SetCategory, Attack.GetAttackEitr and fifteen other methods a second overload, so pre-1.0 plugins patching those by name die on plain 1.0.
The hook. Every Harmony lookup — attribute patches, AccessTools.Method(...) in code, ___field injection — goes through four AccessTools entry points. The patcher detours all four from the preloader, before any plugin loads, and answers the way the plugin's author meant:
- 🚫 A by-name lookup never returns a bridge. The patch lands on the native method vanilla actually calls.
- 🕰️ Between two native overloads, the pre-1.0 one wins. The table comes from a diff of the last pre-1.0 build against 1.0: 18 names whose overload count went from one to several with the old shape still there.
- ↪️ A typed lookup naming a bridge's exact parameters is redirected to the native it forwards to — when the bridge only appends defaulted parameters and Harmony's own patch machinery is asking. The patch then fires on vanilla's own calls. A plugin's own typed lookup gets the bridge it named, so
MethodInfo.Invoke, delegates andTraversekeep the old arity. A bridge handed straight toHarmony.Patchis swapped for the native at patch time. Bridges that transform their arguments are never redirected. - 🧾 A field lookup that finds an alias is answered from the caller's own IL: a plugin compiled against the old shape gets the alias, everyone else the native field.
Anything the hook does not understand falls through to HarmonyX's own behaviour, exception included. The guard still runs at every boot: with the hook in, it is advisory — the log names every plugin whose by-name patch now relies on the hook — and if the hook cannot be installed, it blocks exactly as it would without one. What the hook leaves alone on purpose: plain Type.GetMethod / Type.GetField callers. Which brings us to the next rule.
The return-type rule. An old call site resolves on the full signature, return type included, so restoring Vector2i ZoneSystem.GetZone(Vector3) beside the native Vector2s one is legal — and exactly what those call sites need. But reflection cannot tell the two apart: the moment both exist, every plugin that looks that name up throws AmbiguousMatchException in its own startup and takes itself down. That is not theoretical: an earlier release injected ZDO.GetSector() and GetZone(Vector3) this way, and Wonderland died in Awake() on a live server because its compatibility probe does that lookup. So the patcher refuses any bridge that would differ by return type alone. The cost is real: ZDO.GetSector() and GetZone(Vector3) call sites stay broken (Procedural Roads, for one). The alternative was those plugins not starting at all.
Everything the guard decides is decided per install, from your plugin folder. A different mod list gets a different answer.
A small companion plugin catches what a preloader cannot.
| 💾 Pre-migration world backups | copies <World>.db and <World>.fwl before 1.0 converts an old world to its chunked save format — it converts once, and does not convert back |
| 🧱 Build-category capacity shield | vanilla's selection arrays are sized to the vanilla categories and throw when custom categories push past them; the shield resizes them |
| 🖼️ Build-menu guard | keeps one outdated build-menu mod from taking the whole HUD down with it |
| 📦 Item-collision shield | a mod item that shares its name with new 1.0 content no longer aborts startup on client and server alike |
📖 Read the saga — the item-collision shield, and what it does not fix
ObjectDB.UpdateRegisters builds the item table with Dictionary.Add, so two item prefabs sharing a name throw out of FejdStartup.SetupObjectDB and startup aborts — a server never loads a world, a client menu spams NullReferenceException. Vanilla's own list is unique; the duplicate comes from a mod registering an item under a name 1.0 now uses itself, and Jotunn lets it through (its duplicate check runs against a table that has not been filled yet). The shield drops the later duplicate just before vanilla's loop — the first registration wins, which is vanilla's whenever vanilla owns the name — and logs which item, which mod token, and why. It only ever acts where vanilla would have thrown.
Measured on a 91-plugin dedicated server list with More Ore Deposits installed, same binary, only the switch toggled: off, An item with the same key has already been added and no world ever loads; on, the world comes up, the shield fires once (GoldOre: kept vanilla's, dropped the mod's), and the mod's five deposits are injected.
⚠️ The shield keeps the game up; it does not make the colliding mod right for 1.0. More Ore Deposits' gold deposits then drop vanilla's Deep North gold ore, and its ×20 smelter bonus applies to vanilla's blast furnace too. Read the warning line, then ask that mod's author for a 1.0 build.
Measured against the plugin lists of two real, running modded servers — not curated samples.
| Static: 74 third-party plugins re-resolved | Plugins fully clean | Unresolved game references | Broken Harmony targets |
|---|---|---|---|
| 🔴 Vanilla, no patcher | 37 / 74 | 139 | 36 |
| 🟢 With the patcher | 65 / 74 | 37 | 5 |
| Live boot, the same lists | 🔴 Vanilla | 🟢 With the patcher |
|---|---|---|
Could not find method (a typed patch target is gone) |
10 | 0 |
A patch lost at PatchAll() |
2 | 2, different ones |
TypeLoadException: VTable setup |
36 lines · 14 types · 6 plugins | 0 |
AmbiguousMatchException actually thrown |
0 | 0 |
Not one plugin is worse off than with no patcher at all. ✅
📖 Read the saga — what is behind those numbers
Four Azumatt inventory mods, Jotunn dependants, JereKuusela admin toolkits and fifteen-plus build-piece packs, warts and all. The static check re-resolves every game reference and every Harmony target in each plugin against the patched assemblies, with the hook modelled; the live check boots the same lists on a real Linux dedicated server, patcher out and patcher in. The server's profile was only ever read. These are the figures from when Valheim 1.0 launched; every hotfix since is re-measured, and the per-hotfix numbers are in the changelog.
The two lost patches without the patcher are Mists of Avalor's no-build protection (the bridge and the redirect bring it back) and Advanced Terrain Modifiers' PaintCleared prefix (it names three parameters 1.0 removed — identical either way). The one that appears with the patcher is a SearsCatalog transpiler whose IL pattern no longer exists — SearsCatalog never got that far before, because its SetCategory postfix was ambiguous and the plugin silently applied nothing.
The VTable row is CoreWoodExtras, RustyBuildPieces, SeedBed, Historical Heritage, Ravenwood Random Relics and Procedural Roads, isolated with an A/B on one fixed mod list and one binary — only the switch toggled — so the count belongs to that fix alone.
One exception was found since, and closed: for a time the hook redirected Historical Heritage's own typed AttachArmor lookup to the longer native method, and its Invoke threw where plain 1.0 returned nothing. The hook now hands a plugin's own reflection the bridge it named.
Absence here is a decision, not an oversight.
| Left alone | Why |
|---|---|
Field type and shape changes — ZoneSystem.m_zones and m_locationInstances, VisEquipment's eleven m_*Item slots and m_clothColliders, ItemStand.m_visualName, Minimap.m_explored, Player.m_knownBiome, Ship.m_sailCloth, PlayerProfile.m_playerStats, CharacterDrop.s_pseudoCounter |
a field's declared type cannot be patched around; those mods have to recompile |
Enum renumbering — FileHelpers.FileSource, Piece.PieceCategory |
old numbers are baked into every call site and the methods still exist, so nothing fails and nothing can be intercepted. Only the one method that moved gets a remapping bridge |
ZoneSystem.m_activeArea / m_activeDistantArea |
a field could be injected, but it would read zero forever, and a sweep of radius zero silently does nothing — worse than failing at load |
ZDO.SetSector, ZDOMan.GetSaveClone, ZDOMan.GetPortals |
the storage behind all three changed. An old GetSaveClone stub would tell any mod that asked that the world holds zero objects |
ZNetScene.InActiveArea(…, int), OutsideActiveArea(…, int) |
both took a ring radius 1.0 removed; no forwarding keeps the caller's intent |
ZDO.GetSector(), ZoneSystem.GetZone(Vector3) |
the return-type rule — bridging them breaks reflection for every plugin that looks either name up |
Ship.UpdateSail(float), UpdateSailSize(float) |
the new argument is state the caller computes; there is no faithful default |
| Transpilers whose IL pattern is gone | nothing outside the plugin can restore an instruction sequence it searches for |
ZDOExtraData's seven Get*(ZDOID) accessors |
replaced by one generic GetData; only World Edit Commands uses them, and it is broken for other reasons |
First ten minutes: install with a mod manager, or by hand as below → start the game or the dedicated server → open BepInEx/LogOutput.log → find the patcher's summary line → read any BLOCKED or SKIPPED line.
| In the log | Means |
|---|---|
Injected |
the bridge is in; callers resolve |
present natively |
the running game already has this shape; nothing to do |
SKIPPED |
⚠️ the target shape was not found. On every 1.0 build checked so far this is zero — anything else means the game changed again and this tool needs re-checking |
BLOCKED |
refused on purpose: a return-type-only overload, a plugin reflecting on the aliased field by string, or — only without the hook — a by-name patch it would break. The line names the plugins it protects |
by-name Harmony patch on ... |
advisory: that plugin's patch now depends on the hook. Turn the hook off and these become BLOCKED |
Harmony hook: ... |
one line per resolution the hook made: what was asked, what was answered, why, and by whom |
🛠️ Install it by hand
The zip carries patchers/Valheim10Compatibility.Patcher.dll and plugins/Valheim10Compatibility.Adapter.dll. A mod manager maps each top-level folder onto the BepInEx/ folder of the same name. By hand, copy the DLL from patchers/ into BepInEx/patchers/ and the one from plugins/ into BepInEx/plugins/.
| File | Goes to | Why |
|---|---|---|
Valheim10Compatibility.Patcher.dll |
BepInEx/patchers/ |
BepInEx runs preloader patchers only from patchers/, before the game assemblies load. In plugins/ it never runs, and nothing tells you so. |
Valheim10Compatibility.Adapter.dll |
BepInEx/plugins/ |
An ordinary plugin. In patchers/ it is never loaded. |
🔍 Read a real log
[Info :Valheim10Compatibility] Ambiguity guard: scanned 84 Harmony plugins under '.../BepInEx/plugins' in 1892 ms; 779 by-name patch targets recorded; 17 plugin(s) reference a pre-1.0 field shape this patcher aliases.
[Info :Valheim10Compatibility] Harmony resolution hook installed on HarmonyX 2.9.0.0 (AccessTools.DeclaredMethod, Method, DeclaredField, Field; PatchProcessor..ctor); ...
[Info :Valheim10Compatibility] Injected ZDOMan.FindSectorObjects(Vector2i, int, int, List<ZDO>, List<ZDO>) -> SimulationDistance(area, distantArea, classic: true)
[Info :Valheim10Compatibility] by-name Harmony patch on EffectList.Create in InfinityHammer.dll, MoreVanillaBuildPrefabs.dll: the resolution hook steers it to the native overload, so this bridge is injected anyway.
[Info :Valheim10Compatibility] Injected EffectList.Create(Vector3, Quaternion, Transform, float, int)
[Warning:Valheim10Compatibility] BLOCKED ZoneSystem.GetZone(Vector3) -> Vector2i: this would differ from the existing Vector2s GetZone(...) by RETURN TYPE ALONE ...
[Info :Valheim10Compatibility] assembly_valheim: 52 bridge(s) injected (12 of them beside a by-name Harmony patch that the resolution hook steers past them), 0 already native, 0 skipped (no target on this build), 2 blocked (0 would break a by-name Harmony patch or a reflective field lookup, 2 would be a return-type-only overload).
[Info :Valheim10Compatibility] Harmony hook: AccessTools.DeclaredMethod(Inventory, "Changed") -> Void Inventory.Changed(Boolean, Boolean) [1 bridge overload(s) excluded] <- AzuAutoStore
If the hook line reads NOT installed, the patcher has fallen back to refusing every colliding bridge outright, and says so.
📂 A nonstandard plugin folder
The guard finds your plugins in BepInEx/plugins. If it cannot find the folder, it says so plainly and every overload bridge goes in unprotected. For a nonstandard layout, set the environment variable VALHEIM10COMPAT_PLUGIN_PATH to the plugin folder.
| Works with vanilla players and servers | Install it on the server | Install it on a client |
|---|---|---|
| everything — it touches no network protocol and changes no save format | when the server's mods are the ones broken on 1.0 | when that player's mods are the ones broken on 1.0 |
🤝 No version matching. A patched server and an unpatched client talk to each other exactly as before. The one exception is a server running AzuAntiCheat, whose whitelist checks the Adapter DLL's bytes — see Before You Install.
| Mod | |
|---|---|
| 🛑 Procedural Roads | Partial. Its VTable crash is gone, but it still calls pre-1.0 methods whose return type changed, which no bridge can restore — on a dedicated server its world-generation handler stops the server from opening for joins. Remove it on 1.0 until it has a 1.0 build. |
| 🛑 More and Modified Player Cloth Colliders | Reads a field whose type changed; it cannot be bridged. Remove it on 1.0. |
| ⚠️ More Ore Deposits | Starts again thanks to the item-collision shield, but its gold deposits drop vanilla's gold ore and its ×20 smelter bonus hits the blast furnace. Wait for a 1.0 build. |
| ⚠️ SearsCatalog | Inert on 1.0 with or without the patcher: its transpiler's IL pattern is gone, and the window it changes is hidden by 1.0. |
| Advanced Terrain Modifiers | Its PaintCleared prefix names parameters 1.0 removed — lost either way; the rest applies. |
| 🛡️ AzuAntiCheat | Works; its ServerSync read is bridged. Keep its whitelist to one version of the Adapter DLL. |
| Valheim Cuisine | Its cooking-station patch resolves again. |
| Historical Heritage · Magic Supremacy | Their custom-slot visuals work; their own reflection gets the bridge it asked for. |
| Infinity Hammer · More Vanilla Build Prefabs | Their by-name EffectList.Create patches are steered to the native method, so the bridge goes in beside them. |
| Wonderland | Starts: the return-type rule keeps its compatibility probe from throwing. |
| CoreWoodExtras · RustyBuildPieces · SeedBed · Ravenwood Random Relics | Load again, through the Hoverable default. |
| Jotunn | Works. A Jotunn mod whose item name collides with new 1.0 content is caught by the shield. |
All on by default, and all safe to leave alone. BepInEx/config/Valheim10Compatibility.cfg is written on first run; each switch turns one whole mechanism off if it misbehaves on your list, without removing the patcher. Nothing is server-synced: each machine reads its own file.
🖥️ Local to each install — edit the file, then restart
| Section | Setting | Off means |
|---|---|---|
| Harmony | ResolutionHook |
every bridge beside a by-name patch is refused, and pre-1.0 plugins patching Inventory.Load, SetCategory or GetAttackEitr by name die at PatchAll() as on plain 1.0 |
| Bridges | PieceTableAvailablePiecesAlias |
the fifteen build-piece mods lose their piece lists again — use it if the tab caveat bites |
| Bridges | ZRoutedRpcEverybodyStatic |
ServerSync-based mods that read ZRoutedRpc.Everybody throw MissingFieldException again |
| Bridges | HoverableGetHoverOffsetDefault |
old classes implementing Hoverable fail TypeLoadException: VTable setup again, taking their whole plugin's patches with them |
| Shields | ObjectDBDuplicateItems |
a mod item whose name collides with new 1.0 content aborts startup again, as on plain 1.0 (read by the companion plugin) |
Needs: BepInExPack Valheim 5.4.2360
- Every release — what it changed and what was measured, newest first — is in
CHANGELOG.md, shipped beside this file. - The preloader changes Valheim's assemblies in memory only. No game file on disk is altered, and none is redistributed here.
Made by Wubarrk — ravenirongames.com. License: MIT. Full text in LICENSE.md.
May your mod list survive the update. 🐗