Environment
- Dead Cells: v35 (2026-05-19 build, non-Steam/Goldberg)
- DCCM core: 35.12.8
- Mod: DeadCellsMultiplayerMod 0.8.91 (release v0.4.3, commit 52f383d)
- Network: LAN + VPN-like tunnel (Hamachi-like tunnel), TCP 1234
- Two players, one host / one client
Bug 1 (main): mob sync breaks on large levels — registry_mismatch → missing_sync_id flood
On the client side, right after entering a large level (SewerShort, 140 tracked mobs) the log floods with paired stale-mapping warnings:
[18:19:00 WRN] [MobSync] stale tracked sync mapping syncId=44 localIndex=142 reason=registry_mismatch:45
[18:19:00 WRN] [MobSync] stale tracked sync mapping syncId=45 localIndex=142 reason=registry_mismatch:44
[18:19:00 WRN] [MobSync] stale tracked sync mapping syncId=40 localIndex=141 reason=registry_mismatch:41
[18:19:00 WRN] [MobSync] stale tracked sync mapping syncId=41 localIndex=141 reason=registry_mismatch:40
[18:19:00 WRN] [MobSync] stale tracked sync mapping syncId=142 localIndex=180 reason=registry_mismatch:143
... (142↔143, 151↔152, 158↔159 too)
Every pair has the same localIndex, i.e. the same mob is reachable under two adjacent syncIds: IdToMob[44] = mobX while MobToId[mobX] = 45. Right after that, hits on those mobs fail:
[18:19:06 WRN] [MobSync] mapping mismatch context=hit syncId=46 expectedType=Zombie|Zombie actualType= reason=missing_sync_id
[18:19:09 WRN] [MobSync] mapping mismatch context=hit syncId=38 expectedType=Scorpio|Scorpio actualType= reason=missing_sync_id
... 130+ more across the level (syncIds in host's allocated range)
Observed effect: the client sees mobs that don't take damage / desync. Leaving to the next level (rebuild resets everything) fully heals it. Small levels (PrisonStart, 35 mobs) show only a handful of benign missing_sync_id; the paired registry_mismatch only appears on big levels.
Host side (trace enabled) shows the allocator is now monotonic (35 → 175 → 263 → 313, the nextRuntimeSyncId=0 reset from the old build is gone — good), so this is a remaining bug in the bind path: during multi-round MOBREG/MOBSTATE binding on a 140-mob level, one mob ends up bound to two adjacent syncIds. Suspect TryBindUnboundMobByTypeAndSpawnLocked — it skips mobs that are already bound (MobToId check) but never checks whether the target syncId is already occupied, and the one-shot state bind (state_oneshot_bind) plus MOBREG re-broadcast can race on nearby same-type mobs.
Bug 2: client reconnect cannot resume the current run (falls back to its own stale save)
Client is non-authoritative, so SaveGuard blocks every client world save during the run:
[18:44:59] [NetMod][SaveGuard] skipped non-authoritative client world write before host-disconnect auto-exit (host_disconnect)
After a disconnect and reconnect, the client auto-starts from its own local save ([NetMod] Auto-started new game after seed, [MobSync] ... role=client), which is always an older level than the host's current one (host was in BeholderPit, client landed in PrisonCourtyard, plus [NetMod] Remote level graph not applied for PrisonCourtyard: remote graph not received). The host's Coop ghost runtime bootstrapped for continue/level hero=880 level=BeholderPit only restores the host itself — it does not steer the client to the host's level. So reconnecting mid-run cannot continue the same progress; the only reliable path is to start a brand-new run.
Also, when the host is already inside a run and the client reconnects (host did not restart), the client just sits in the lobby and never enters the run.
It would be great to have the host re-send the current level/seed state on reconnect so the client can rejoin the host's current level.
Bug 3 (minor): host opening the main menu mid-run crashes the client
[18:43:03] [NetMod] Main menu opened during run; stopping network → client goes through host-disconnect auto-exit → level dispose → game re-init → Fatal Error:
[18:45:13 FTL][GameStartup] Fatal Error
Hashlink.Marshaling.HashlinkError: Uncaught hashlink exception.Null access .isEnable
at Haxe!hxd.App.mainLoop ...
at ModCore.Modules.GameStartup.StartGame()
Crash happens at GameStartup.StartGame() right after the forced exit/continue bootstrap — probably a null UI/game object after the non-authoritative save was skipped.
Logs
Full trace logs (host 7.9 MB, client 96 KB) available on request — happy to share the exact files.
Environment
Bug 1 (main): mob sync breaks on large levels —
registry_mismatch→missing_sync_idfloodOn the client side, right after entering a large level (SewerShort, 140 tracked mobs) the log floods with paired stale-mapping warnings:
Every pair has the same localIndex, i.e. the same mob is reachable under two adjacent syncIds:
IdToMob[44] = mobXwhileMobToId[mobX] = 45. Right after that, hits on those mobs fail:Observed effect: the client sees mobs that don't take damage / desync. Leaving to the next level (rebuild resets everything) fully heals it. Small levels (PrisonStart, 35 mobs) show only a handful of benign
missing_sync_id; the pairedregistry_mismatchonly appears on big levels.Host side (trace enabled) shows the allocator is now monotonic (35 → 175 → 263 → 313, the nextRuntimeSyncId=0 reset from the old build is gone — good), so this is a remaining bug in the bind path: during multi-round MOBREG/MOBSTATE binding on a 140-mob level, one mob ends up bound to two adjacent syncIds. Suspect
TryBindUnboundMobByTypeAndSpawnLocked— it skips mobs that are already bound (MobToIdcheck) but never checks whether the target syncId is already occupied, and the one-shot state bind (state_oneshot_bind) plus MOBREG re-broadcast can race on nearby same-type mobs.Bug 2: client reconnect cannot resume the current run (falls back to its own stale save)
Client is non-authoritative, so
SaveGuardblocks every client world save during the run:After a disconnect and reconnect, the client auto-starts from its own local save (
[NetMod] Auto-started new game after seed,[MobSync] ... role=client), which is always an older level than the host's current one (host was inBeholderPit, client landed inPrisonCourtyard, plus[NetMod] Remote level graph not applied for PrisonCourtyard: remote graph not received). The host'sCoop ghost runtime bootstrapped for continue/level hero=880 level=BeholderPitonly restores the host itself — it does not steer the client to the host's level. So reconnecting mid-run cannot continue the same progress; the only reliable path is to start a brand-new run.Also, when the host is already inside a run and the client reconnects (host did not restart), the client just sits in the lobby and never enters the run.
It would be great to have the host re-send the current level/seed state on reconnect so the client can rejoin the host's current level.
Bug 3 (minor): host opening the main menu mid-run crashes the client
[18:43:03] [NetMod] Main menu opened during run; stopping network→ client goes through host-disconnect auto-exit → level dispose → game re-init → Fatal Error:Crash happens at
GameStartup.StartGame()right after the forced exit/continue bootstrap — probably a null UI/game object after the non-authoritative save was skipped.Logs
Full trace logs (host 7.9 MB, client 96 KB) available on request — happy to share the exact files.