

Fix for the long-running Valheim bug where players, carts, or other floating objects can stay stuck in water/swimming/tarred state after leaving a liquid trigger, teleporting, changing scene, or moving below the old liquid surface height.

This mod targets cases such as:
The bug can happen in vanilla, and some modded setups appear to make it easier to trigger.
Valheim liquid volumes keep an internal list of objects currently considered inside water or tar. If OnTriggerExit is missed, or the liquid counter becomes unbalanced, an old WaterVolume or LiquidSurface can keep calling SetLiquidLevel(...) on an object that has already left.
That stale liquid level can then make the player swim or become tarred again when they move to a lower height.
This mod adds several guards:
WaterVolume and LiquidSurface before vanilla floater updates run.Good reproduction targets:
For testing, enable:
DebugLogging = true
Useful log lines include:
Rejected SetLiquidLevel
Purged stale Water entry
Purged stale Tar entry
Emergency-cleared player liquid state
Turn debug logging off after testing, because liquid-related events can be frequent.
This mod does not add new world objects, items, prefabs, RPC gameplay features, or save data. It only patches runtime liquid state handling.
If you are testing whether the fix is responsible for preventing the bug, you can turn off most protection without removing the DLL:
ValidateSetLiquidLevelSource = false
PurgeStaleSourceLists = false
EmergencyClearPlayers = false
EmergencyClearFloatingObjects = false
ClampLiquidCounters = false
ResetOnTeleportStart = false
The Harmony patches remain loaded, but with those settings disabled the mod mostly passes vanilla behavior through.
[Debug]
## Log rejected liquid updates and purged stale entries. Keep disabled during normal play. [Not Synced with Server]
# Setting type: Boolean
# Default value: false
DebugLogging = false
## Seconds between repeated rejected SetLiquidLevel logs for the same target/source/liquid/reason while DebugLogging is enabled. [Not Synced with Server]
# Setting type: Single
# Default value: 1
RejectedSetLiquidLevelLogInterval = 1
[General]
## If true, synced configuration is locked and can only be changed by the server.
# Setting type: Boolean
# Default value: true
LockConfiguration = true
## Reject positive water/tar level updates from a liquid source whose trigger bounds no longer touch the target. [Synced with Server]
# Setting type: Boolean
# Default value: true
ValidateSetLiquidLevelSource = true
## Before WaterVolume/LiquidSurface updates floaters, remove stale IWaterInteractable entries that are no longer inside that source. [Synced with Server]
# Setting type: Boolean
# Default value: true
PurgeStaleSourceLists = true
## If a player still has water/tar level but no nearby current liquid trigger, clear the stuck state. [Synced with Server]
# Setting type: Boolean
# Default value: true
EmergencyClearPlayers = true
## Apply the same emergency clear to Floating objects such as carts/items. [Synced with Server]
# Setting type: Boolean
# Default value: true
EmergencyClearFloatingObjects = true
## Clamp Character/Floating liquid reference counters so missed trigger exits cannot leave negative or stale counts. [Synced with Server]
# Setting type: Boolean
# Default value: true
ClampLiquidCounters = true
## Clear player water/tar state when Player.TeleportTo starts. Current liquid triggers will restore it if the destination is actually in liquid. [Synced with Server]
# Setting type: Boolean
# Default value: true
ResetOnTeleportStart = true
[Tuning]
## Meters added to source and target bounds during source validation. Raise slightly if legitimate shallow water is rejected. [Synced with Server]
# Setting type: Single
# Default value: 0.75
SourceBoundsPadding = 0.75
## Extra meters used when scanning around the target collider for any current liquid trigger. [Synced with Server]
# Setting type: Single
# Default value: 0.35
EmergencyProbePadding = 0.35