Handle keepalive packets in server loading state#864
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the server’s ServerLoading connection state to accept and process Client_KeepAlive packets (matching ServerPlaying behavior) to prevent “unhandled packet” log spam during reconnect/loading scenarios (Fixes #861).
Changes:
- Add a typed handler for
ClientKeepAlivePacketinServerLoadingStateand update latency/behind/simulation bookkeeping similarly toServerPlayingState. - Add a regression test client-state that sends a keepalive during the loading phase while the server is blocked waiting for join point completion.
- Add a server-side test covering the reconnect/loading keepalive scenario.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| Source/Common/Networking/State/ServerLoadingState.cs | Adds Client_KeepAlive handling during ServerLoading to avoid unhandled packet errors and keep timing/latency fields updated. |
| Source/Tests/ServerTest.cs | Adds a regression test intended to validate keepalive handling while waiting for join point completion. |
| Source/Tests/Helper/TestLoadingKeepAliveState.cs | Implements a test client connection state that sends Client_KeepAlive during the loading handshake. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| var timeoutWatch = Stopwatch.StartNew(); | ||
| while (true) | ||
| { | ||
| if (server.playerManager.Players.Count == 0) | ||
| break; | ||
|
|
||
| if (timeoutWatch.ElapsedMilliseconds > 2000) | ||
| Assert.Fail("Timeout"); | ||
|
|
||
| Thread.Sleep(50); | ||
| } |
There was a problem hiding this comment.
The success condition here can be satisfied immediately because Players.Count starts at 0; the loop may break on the first iteration before the client has connected/sent any packets, so the test can pass without exercising the keepalive-in-loading behavior. Consider waiting until the player count becomes >0 (or another “client reached loading” signal) and only then waiting for it to drop back to 0 after disconnect.
| _ = Task.Run(async () => | ||
| { | ||
| await Task.Delay(200); | ||
| server.worldData.EndJoinPointCreation(); | ||
| }); |
There was a problem hiding this comment.
This fire-and-forget Task.Run isn’t awaited or tracked, so it can still execute after the test completes/teardown starts, potentially making the test nondeterministic and leaking work into subsequent tests. Store the task and await it (or add it to teardown) so join point completion is guaranteed and scoped to the test.
| await TypedPacket<ServerJoinDataPacket>(); | ||
|
|
||
| connection.Send(Packets.Client_WorldRequest); | ||
| connection.Send(new ClientKeepAlivePacket(0, 0, false, 0), false); |
There was a problem hiding this comment.
Client_WorldRequest is sent reliably, but the keepalive is sent unreliably (reliable: false), so it can be delivered/processed before the world request and state transition on some transports. That can make the regression test flaky and may not guarantee the keepalive is handled specifically in ServerLoading. For determinism, send the keepalive reliably here (or otherwise synchronize so it’s sent only after the server has switched to loading).
| connection.Send(new ClientKeepAlivePacket(0, 0, false, 0), false); | |
| connection.Send(new ClientKeepAlivePacket(0, 0, false, 0)); |
e32e439 to
86c0c6a
Compare
Summary
Handle
Client_KeepAlivepackets while the connection is inServerLoading.Fixes #861.
What changed
ServerLoadingStateServerPlayingStateWhy this fixes the issue
Previously, a reconnecting client could continue sending keepalive packets while the server was still in
ServerLoading, and the server would log them as unhandled packets.After this change, the loading state accepts those packets normally, which removes the server log spam and keeps connection bookkeeping coherent during reconnect/loading.
Validation
dotnet test Source/Tests/Tests.csproj --filter "FullyQualifiedName~LoadingStateHandlesKeepAliveWhileWaitingForJoinPoint"dotnet build Source/Server/Server.csproj -c Release