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.
ClientGhostWatchdog
Client-side watchdog mod for players connecting to Valheim servers that detects dropped connections and disconnects cleanly to prevent ghost desyncs.
| Date uploaded | 2 weeks ago |
| Version | 1.1.3 |
| Download link | DreamWraith-ClientGhostWatchdog-1.1.3.zip |
| Downloads | 99 |
| Dependency string | DreamWraith-ClientGhostWatchdog-1.1.3 |
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.2350README
ClientGhostWatchdog
A standalone, 100% client-side watchdog mod for players connecting to remote/dedicated Valheim servers that prevents "Zombie Client" / "Ghost Connection" desync states.
[!NOTE] Client-Side Only: This mod is installed locally on your PC. It does not need to be installed on the server and works when connecting to any dedicated or community server.
The Problem
When playing on a dedicated or remote server, if the server drops your client's connection (for example, during daily scheduled server restarts, network glitches, or ISP timeouts with a ClosedByPeer status), vanilla Valheim has a critical client-side bug:
ZNet.UpdatePeers()ignores disconnection error codes reported byZRpc.Update().ZSteamSocket.IsConnected()only checks pointer validity, remaining "connected" even after the socket closed.- The client's
ZNet.m_connectionStatusremainsConnected. - The client continues running in an empty, desynced world without realizing it was disconnected. Any structures built, items moved, or actions taken while desynced are lost or result in desync rollbacks.
The Solution
ClientGhostWatchdog runs an independent background coroutine on your game client when connecting to a remote server:
- No
Game.UpdateOverhead: Runs completely via a dedicated Unity coroutine with real-time sleep intervals, executing zero per-frame checks inGame.Update. - Disabled on Local Worlds: Strictly inactive on singleplayer worlds or local server hosts (
ZNet.instance.IsServer()). No coroutine is started and no checks run. - Two-Stage Detection:
- Stage 1 (Halfway Warning): When server silence reaches halfway through the configured timeout period (e.g. 15s at the default 30s timeout), a prominent on-screen center notice warns the player:
"Server connection lost, attempting to reconnect... ({0}s)". The{0}placeholder dynamically updates with the countdown until disconnect. - Auto-Recovery: If server communication resumes during the warning stage, the warning is dismissed and an on-screen notice confirms:
"Server connection restored." - Stage 2 (Timeout & Clean Logout): If silence reaches the full timeout, the server socket closes, or the server peer drops, the mod displays
"Server connection lost. Disconnecting...", pauses for the configured notice delay (DisconnectDelaySeconds, default 5s) so the notice is readable, and triggersGame.instance.Logout().
- Stage 1 (Halfway Warning): When server silence reaches halfway through the configured timeout period (e.g. 15s at the default 30s timeout), a prominent on-screen center notice warns the player:
- Severed Peer & badly behaved Mod Protection: Catches scenarios where a third-party mod (such as
ServerCharactersinventory sync timeout) or network failure drops the server peer without an engine logout, preventing players from being trapped in desync limbo. - Preserves Native Disconnect Reasons: If an admin kicks or bans a player, or the server shuts down, the watchdog defers to native Valheim, allowing players to see the genuine error dialog instead of generic watchdog notices.
- Portal & Teleport Hang Guard: Resumes health checks after a configurable ceiling (
TeleportTimeoutSeconds, default 30s) if a player gets stuck in an indefinite portal desync. - Preserves Player Progress: Calling
Game.instance.Logout()ensures the client cleanly saves their character profile (SavePlayerProfile(setLogoutPoint: true)) before returning to the main menu. - Informs the Player: Displays an informative dialog on the main menu informing the player why they were returned to the menu and that their local save was preserved.
- Defensive Lifecycle & State Safety: Centralized state machine (
WatchdogManager) with atomic, TTL-backed disconnect reasons (DisconnectReasonManager) to prevent stale error dialogs across scene transitions.
Design Philosophy & Tradeoffs
This mod intentionally enforces a fail-safe disconnect policy:
- Why fail-safe disconnect?: In vanilla Valheim, hanging onto an unacknowledged dead connection risks losing valuable building, harvesting, or combat progress. By forcing a clean logout via
Game.instance.Logout(), the mod saves the player's local inventory and state immediately before the desync worsens. - Handling unstable connections: If you frequently play on high-latency satellite connections, fluctuating cellular networks, or Wi-Fi prone to multi-second packet drops:
- Increase
TimeoutSeconds(e.g. to60or90seconds). The halfway warning will scale accordingly (30s warning for a 60s timeout), giving your connection ample time to recover before any disconnect is triggered. - You can also set
TimeoutSecondsto0to completely disable the watchdog without removing the mod.
- Increase
Configuration
The configuration file is automatically generated at BepInEx/config/dreamwraith.ClientGhostWatchdog.cfg:
| Section | Setting | Type | Default | Description |
|---|---|---|---|---|
1 - General |
TimeoutSeconds |
float |
30 |
Total seconds of server silence before disconnecting. Halfway through (e.g. 15s), a warning notice is displayed. Range: 0 to 300. Set to 0 to disable the watchdog. |
1 - General |
CheckIntervalSeconds |
float |
1.0 |
Interval in seconds between watchdog checks in the background coroutine. Range: 1.0 to 5.0. |
1 - General |
TeleportTimeoutSeconds |
float |
30 |
Maximum seconds a player can remain in a teleporting state before watchdog health checks resume. Prevents infinite limbo if a portal hangs. Range: 0 to 180. Set to 0 to disable ceiling. |
2 - On-Screen Notices |
WarningNoticeMessage |
string |
"Server connection lost, attempting to reconnect... ({0}s)" |
Large on-screen center notice displayed at the halfway mark. {0} is replaced with remaining seconds. |
2 - On-Screen Notices |
RecoveredNoticeMessage |
string |
"Server connection restored." |
Large on-screen center notice displayed if connection recovers before timeout. |
2 - On-Screen Notices |
DisconnectNoticeMessage |
string |
"Server connection lost. Disconnecting..." |
Large on-screen center notice displayed when timeout is reached. |
2 - On-Screen Notices |
DisconnectDelaySeconds |
float |
5.0 |
Seconds to wait after displaying the disconnect notice before executing logout, giving players time to read the notice. Range: 0 to 30. Set to 0 for instantaneous logout. |
3 - Main Menu Dialog |
ShowDisconnectReason |
bool |
true |
Whether to display an explanatory message on the main menu after being disconnected by this watchdog. |
3 - Main Menu Dialog |
DisconnectMessage |
string |
"Server connection lost (Ghost connection prevented). Progress was saved locally." |
Custom message displayed on the main menu dialog when disconnected. |
4 - Debug |
EnableDebugLogs |
bool |
false |
When enabled, prints detailed debug logs with every watchdog check tick, timer event, threshold evaluation, and connection lifecycle hook. |
4 - Debug |
DebugSimulateSeverKey |
KeyCode |
None |
DEBUG ONLY: Keybind to simulate an abrupt peer disconnection (ServerCharacters behavior). Requires EnableDebugLogs to be true. Keep as None for normal play. |
Installation
[!TIP] Client-Side Only: Install this on your local PC via your mod manager (Gale / r2modman / Thunderstore) or manual drop. Nothing needs to be installed on the server.
- Ensure BepInExPack Valheim is installed on your client.
- Install via Gale / r2modman, or manually copy
ClientGhostWatchdog.dllinto yourValheim/BepInEx/plugins/folder. - Launch Valheim and connect to your server.
Building from Source
The project uses a portable MSBuild configuration that auto-detects standard Steam paths.
dotnet build -c Release
The compiled assembly will be placed in bin/Release/net48/ClientGhostWatchdog.dll. Building in Release configuration also automatically packages the distribution ZIP to bin/Publish/ClientGhostWatchdog-<Version>.zip.
Custom & CI Paths
For custom installations or CI/CD pipelines, you can specify paths using environment variables or a local .user property file:
- Environment Variables:
VALHEIM_PATH: Absolute path to your Valheim game root directory.BEPINEX_PATH: Absolute path to your BepInExcorefolder.
- Local User Override:
Copy
ClientGhostWatchdog.csproj.user.exampletoClientGhostWatchdog.csproj.userin the project root (this file is git-ignored and automatically loaded by MSBuild):<?xml version="1.0" encoding="utf-8"?> <Project> <PropertyGroup> <GamePath>C:\Games\Valheim</GamePath> <BepInExCorePath>C:\Games\Valheim\BepInEx\core</BepInExCorePath> </PropertyGroup> </Project>
Packaging, Publishing & Releases
All developer automation tools for release management, packaging, and publishing to Thunderstore, Hexium, and Nexus Mods are consolidated into the single PowerShell 7 entrypoint .scripts/modtools.ps1, powered by the shared DW.ValheimModTools module:
- Release Management: pwsh ./.scripts/modtools.ps1 release compiles in Release, creates mod & source archives, extracts changelog notes, and publishes GitHub Releases (Draft by default, or published with -Publish) using the gh CLI.
- Packaging & Version Bumping: pwsh ./.scripts/modtools.ps1 package -Bump Patch increments SemVer in ClientGhostWatchdog.csproj, updates manifest.json, and bundles distribution archives.
- Portal Publishing: pwsh ./.scripts/modtools.ps1 publish -Target All uploads directly to Thunderstore, Hexium, and Nexus Mods APIs.
- CI/CD Workflow: .github/workflows/publish.yml provides an automated GitHub Actions workflow to publish to Thunderstore, Hexium, and Nexus Mods whenever a GitHub Release is published.
For detailed documentation on flags, workflows, and secret configuration, see .scripts/README.md.
License, Author Notes
- This project is licensed under the GNU General Public License v3.0 - see the LICENSE.md file for details.
- A Small Note from Me about Valheim Modding Specifically - Thoughts on open-source modding, community inclusivity, and anti-gatekeeping.
- A Note on AI, Software Craft, and Why This Code Exists - Personal essay on software craft, human agency, and engineering responsibility.
CHANGELOG
Changelog
All notable changes to ClientGhostWatchdog will be documented in this file.
[1.2.0] - 2026-10-19
Changed
- Switch to compile-time publicized game assemblies via Jötunn prebuild task (
DoPrebuild.props,JotunnLib). - Use
nameof(...)for Harmony patch method targets acrossZNet,Game,FejdStartup, andTerminal. - Remove Hexium publish target and tooling.
- Simplified Unity and config null checks.
[1.1.3] - 2026-10-08
Changed & Improved
- Centralized build and release automation tooling to shared
DW.ValheimModTools. - Added support for automated Nexus Mods packaging and publishing.
[1.1.2] - 2026-10-08
Changed & Improved
- Fixed readme issues.
[1.1.1] - 2026-10-08
Changed & Improved
- Added detection and coexistence logging for
NetworkPerformanceSystem(MidnightsFX.NetworkPerformanceSystem). - Modernized project packaging and release automation tooling.
- Readme Cleanup
[1.1.0] - 2026-09-17
Fixed & Improved
- My Very Lame Peer Check: I had a dumb
if (serverPeer == null) continue;check thinking it would just wait for the peer to hook up. Turns out, when a mod likeServerCharactersabruptly yanks the cord on your connection,GetServerPeer()becomesnull, which meant my watchdog was happily spinning in an infinitecontinue;loop doing nothing while players were stranded in ghost limbo. I misread the underlying behavior on this one. If you're spawned in the world and the server peer vanishes, it now catches it, saves your character, and pulls you back to the menu. - Unswallowing Native Disconnects: I was being a bit too eager with my custom popup and accidentally bulldozing native's actual error messages. If an admin kicked or banned you, or the server shut down, my code was just crapping all over it to tell you it saved you from a ghost connection instead of letting you know the actual disconnect reason. Now checks if the game already has a legitimate error status before putting my message over it.
- Cleaned Up Connection Status Checks: Cleaned up my previously messy checks. Instead of silently dying in the background if the connection dropped, it now distinguishes between pre-spawn loading and in-game disconnects.
- Teleport / Portal Hang Guard: If a portal handshake hangs or desyncs, the watchdog shouldn't stay muted forever. Added a
TeleportTimeoutSecondsconfig (defaults to 30s) that resumes health checks if you get stuck in an infinite portal transition. - Disconnect Notice Delay: Added
DisconnectDelaySeconds(defaults to 5s, range 0 to 30s) so the on-screen center notice actually stays up long enough to read what happened before getting booted to the main menu. Set to 0 for instantaneous logout. - Debug Peer Severance Testing: Added
DebugSimulateSeverKeykeybind andcgw_simulateseverconsole command (requiresEnableDebugLogs) to simulate badly behaved mod peer drops for testing without needing another mod installed. - Consolidated Disconnect Logic: Cleaned up the disconnect code into a single shared helper routine instead of repeating HUD notices and logout calls all over the place.
[1.0.0] - 2026-09-17
Initial Release
- Dedicated Watchdog Coroutine: Background heartbeat monitoring independent of
Game.Updateto prevent frame hitching. - Singleplayer / Host Safety: Automatically inactive on singleplayer sessions or when hosting local servers (
ZNet.instance.IsServer()). - Connection Loss Detection: Monitors
ZRpcsocket peer state and detects server disconnection, timeout silence, and socket closure. - Midpoint Warning Notice: Displays a customizable on-screen warning alert (TopCenter hud notice) before forcing disconnection.
- Clean Disconnect: Triggers graceful return to main menu (
FejdStartup) preserving player save integrity and preventing ghost connection rollbacks. - Configurable: Configurable timeout duration, check intervals, custom warning/disconnect messages, and debug logging.