You are viewing a potentially older version of this package. View all versions.
DreamWraith-ClientGhostWatchdog-1.1.3 icon

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-5.4.2350 icon
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.2350

README

ClientGhostWatchdog

GitHub Release GitHub Downloads Last Commit Publish Status Thunderstore Hexium Nexus Mods Game: Valheim BepInEx Pack Target: .NET 4.8 Client Side Only License AI Philosophy

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:

  1. ZNet.UpdatePeers() ignores disconnection error codes reported by ZRpc.Update().
  2. ZSteamSocket.IsConnected() only checks pointer validity, remaining "connected" even after the socket closed.
  3. The client's ZNet.m_connectionStatus remains Connected.
  4. 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.Update Overhead: Runs completely via a dedicated Unity coroutine with real-time sleep intervals, executing zero per-frame checks in Game.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 triggers Game.instance.Logout().
  • Severed Peer & badly behaved Mod Protection: Catches scenarios where a third-party mod (such as ServerCharacters inventory 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. to 60 or 90 seconds). 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 TimeoutSeconds to 0 to completely disable the watchdog without removing the mod.

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.

  1. Ensure BepInExPack Valheim is installed on your client.
  2. Install via Gale / r2modman, or manually copy ClientGhostWatchdog.dll into your Valheim/BepInEx/plugins/ folder.
  3. 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 BepInEx core folder.
  • Local User Override: Copy ClientGhostWatchdog.csproj.user.example to ClientGhostWatchdog.csproj.user in 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

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 across ZNet, Game, FejdStartup, and Terminal.
  • 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 like ServerCharacters abruptly yanks the cord on your connection, GetServerPeer() becomes null, which meant my watchdog was happily spinning in an infinite continue; 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 TeleportTimeoutSeconds config (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 DebugSimulateSeverKey keybind and cgw_simulatesever console command (requires EnableDebugLogs) 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.Update to prevent frame hitching.
  • Singleplayer / Host Safety: Automatically inactive on singleplayer sessions or when hosting local servers (ZNet.instance.IsServer()).
  • Connection Loss Detection: Monitors ZRpc socket 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.