Host cannot accept Steam player after reconnect (e.g. Fix & Restart) - #958
Host cannot accept Steam player after reconnect (e.g. Fix & Restart)#958romangr wants to merge 4 commits into
Conversation
…e-prompt The P2P session request handler only removed a Steam ID from session.pendingSteam on accept. A rejected (or disconnected-before-accept) player left a stale entry, and the guard then skipped the handler on every reconnect, leaving the host without a prompt and the player stuck "waiting for host to accept". Clear the entry on reject and scope the dedup guard to the prompt enqueue so pendingSteam membership tracks exactly an unanswered prompt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011htpmJM4qmJhthgQ9djDWT
SteamBaseConn.OnClose previously left the Steam P2P session open (TODO), so after a client left, the host kept a half-open session with their Steam ID. On reconnect Steam reused that session instead of posting a fresh P2PSessionRequest_t, so the host never got a new accept prompt and the client sat on "waiting for host to accept". Free the session via CloseP2PSessionWithUser in OnClose, and also in SteamServerConn's P2P timeout/error path, which tears the connection down through SetDisconnected without going through OnClose. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011htpmJM4qmJhthgQ9djDWT
When a joiner reconnects before the host has torn down their previous connection (a fast rejoin, before the Steam P2P timeout fires), their new join packet arrives on the still-open session and previously hit the "shouldn't happen" branch in SteamP2PNetManager.Tick, where it was dropped. The client never resends its join packet, so the joiner stayed stuck on "waiting for host to accept". Treat a join packet from an already-known remote as a reconnect: drop the stale player via SetDisconnected and fall through to the normal accept path. SetDisconnected (not Close) is used deliberately so OnClose -> CloseSteamSession doesn't tear down the shared P2P session the new join just arrived on. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011htpmJM4qmJhthgQ9djDWT
Closing the session in OnClose right after queueing the goodbye dropped it: SendP2PPacket only queues, and CloseP2PSessionWithUser discards queued unsent packets. Clients never received server-initiated disconnect reasons over Steam (wrong password, kick, username taken) and fell back to a generic timeout. Keep the immediate close for the no-goodbye paths, but when a goodbye was sent, defer CloseP2PSessionWithUser by 3 seconds via Task.Delay + server.Enqueue so it runs on the server thread after Steam has flushed the packet. Skip the deferred close if a connection with the same remoteId exists in playerManager.Players by then: the old player was already removed, so any match is a fast rejoin riding the same underlying session, and closing would tear it down. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@notfood please take a look |
|
I discussed it with Mibac, a more robust approach is to close any stale connection upon the arrival of a new client sharing the same identification clues. This logic can be applied uniformly, including to non‑Steam clients. |
|
Thanks — happy to go that way. Before I write it, one trade-off I'd rather have your call on, since the "identification clues" differ in how much we can trust them. Steam gives us a
Worth weighing against how much it buys for direct clients: a reconnecting direct client gets its own Either way I'd extract the policy into one Separately, one thing I found that keeps the Steam-side code needed regardless: Which way do you and Mibac want the username case? |
Fix Steam P2P fast rejoin
Fixing #843
Fast rejoin dropped
When a joiner reconnects before the host has torn down their previous connection (before the Steam P2P timeout fires), the new join packet arrives on the still-open session and hit the "shouldn't happen" branch in SteamP2PNetManager.Tick, where it was dropped. The client never resends its join packet, so the joiner stayed stuck on "waiting for host to accept".
Now a join packet from an already-known remote is treated as a reconnect: the stale player is dropped via SetDisconnected and we fall through to the normal accept path. SetDisconnected is used instead of Close so OnClose → CloseSteamSession doesn't tear down the shared P2P session the new join just arrived on.
Disconnect reasons delivered
Closing the session in OnClose immediately after queueing the goodbye dropped it: SendP2PPacket only queues, and CloseP2PSessionWithUser discards queued unsent packets. Clients never received server-initiated disconnect reasons (wrong password, kick, username taken) and fell back to a generic timeout.
The immediate close is kept for the no-goodbye paths. When a goodbye was sent, CloseP2PSessionWithUser is deferred by 3 seconds via Task.Delay + server.Enqueue so it runs on the server thread after Steam has flushed the packet. The deferred close is skipped if a connection with the same remoteId exists in playerManager.Players by then — the old player was already removed, so any match is a fast rejoin riding the same underlying session, and closing would tear it down.
Testing