Skip to content

Fix: player session never recovers when server resets - #18

Closed
karyao wants to merge 16 commits into
mainfrom
fix-12-player-session-never-recovers-after-server-restart
Closed

Fix: player session never recovers when server resets#18
karyao wants to merge 16 commits into
mainfrom
fix-12-player-session-never-recovers-after-server-restart

Conversation

@karyao

@karyao karyao commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Closes #12

Description:

Server restarting previously left players stuck on "Reconnecting.,,"because the browser still had player ID cookie, but the restarted server no longer had the corresponding player session. To fix this:

  • on graceful shutdown, server notifies connected clients before exiting. Players are returned to the registration page, old player ID is cleared, and previous entered name is pre-filled.
  • if a player's websocket connection fails 3 times, the client treats the saved player session as expired automatically re-registers the player again.
  • if automatic registration fails, then old credentials are cleared and players are sent to the registration page.
  • shutdown and critical socket updates are prioritized so they are not blocked behind queued map updates.

@karyao
karyao marked this pull request as draft August 19, 2026 01:43
@karyao
karyao force-pushed the fix-12-player-session-never-recovers-after-server-restart branch from 2c598a9 to 7a8c616 Compare August 19, 2026 07:38
@karyao
karyao force-pushed the fix-12-player-session-never-recovers-after-server-restart branch from a149dc3 to f72fbea Compare August 19, 2026 09:22
@karyao
karyao marked this pull request as ready for review August 19, 2026 09:27
@karyao
karyao requested a review from jbriones1 August 19, 2026 09:31
@karyao

karyao commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

@jbriones1

image

@jbriones1 jbriones1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test comments can probably be ignored, but when tests are sus I'm sus of the code.

Comment thread frontend/src/app/core/sockets/game-socket.service.spec.ts
Comment thread frontend/src/app/core/sockets/game-socket.service.spec.ts Outdated
});

it('re-registers with the saved name and reconnects', async () => {
playerName.get.mockReturnValue('Odin');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doxxing my son.

Comment thread frontend/src/app/pages/game-page/game-page.component.spec.ts Outdated
Comment thread frontend/src/app/pages/game-page/game-page.component.spec.ts Outdated
Comment thread frontend/src/app/core/sockets/game-socket.service.ts

const startCalls = gameSocket.start.mock.calls;
const onConnected = startCalls[startCalls.length - 1][1] as () => void;
expect(startCalls[startCalls.length - 1][0]).toBe('NEWID');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this doesn't check the third argument of the start function. In the future, if someone refactors and messes up and doesn't add a third parameter on reconnect this test would fail.


protected override shouldReconnect(closeEvent: CloseEvent): boolean {
if (this.mode !== 'player') {
return true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think players should reconnect if the closeEvent was graceful (status 1001). I don't think our server is capable of having a good shutdown, so maybe this could be added.

With this logic, if server is shut down and someone never closed their browser tab, they would reconnect if the server was turned back and they opened their tab. Would be kinda weird.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Server shutdown now sends an explicit shutdown command soclient can now distinguish between intentional game shutdown from the other 1001 graceful closes

Comment thread frontend/src/app/core/credentials.service.spec.ts Outdated
Comment thread frontend/src/app/core/player-name.service.ts Outdated
@karyao

karyao commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Will take a look at how to get the server to explicitly send signals instead of frontend guessing when to reconnect so we're not retrying connection even on graceful server shutdowns!

@karyao
karyao marked this pull request as draft August 24, 2026 04:52
@karyao
karyao force-pushed the fix-12-player-session-never-recovers-after-server-restart branch from bdb0c24 to 461f103 Compare August 25, 2026 22:54
@karyao
karyao marked this pull request as ready for review August 26, 2026 22:00
@karyao
karyao requested a review from jbriones1 August 26, 2026 23:31

@jbriones1 jbriones1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I don't understand it, the control functions should only be used for shutdowns, not anything else. Also, I'll need to confirm the Websocket 1001 close message, because I'm not sure this does it.

Comment thread api/hub.go Outdated
message, ok := informMessage(player, coordinate)
if ok {
h.broadcast(message, nil, onlyViewers)
h.broadcastControl(message, nil, onlyViewers)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems dangerous. Other player messages could be waiting in the queue to be written, but someone disconnecting could remove all those messages. I think this should be a normal broadcast.

Comment thread api/hub.go Outdated
outgoing = message
}
if !h.enqueue(connection, outgoing) {
if !h.enqueueControl(connection, outgoing) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, other messages can still be relevant.

Comment thread api/hub.go Outdated
message, ok := informMessage(player, coordinate)
if ok {
h.broadcast(message, nil, onlyViewers)
h.broadcastControl(message, nil, onlyViewers)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, other messages can still be relevant.

Comment thread api/hub.go
Comment thread main.go
Comment on lines +93 to +103
// Block until SIGINT (Ctrl+C) or SIGTERM (systemd stop/restart).
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit

fmt.Println("Shutdown signal received. Notifying players...")
sock.BroadcastShutDown(api.CMD_SHUTDOWN)

// Give write pumps ~1 second to flush the shutdown message before exiting.
time.Sleep(1 * time.Second)
fmt.Println("Server exiting.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems suspicious since:

  1. The HTTP listener is still active while shutting down, so new connections can be accepted while attempting to shut down.
  2. It sleeps for 1 second, which is arbitrary, a deadline for WriteControl should be used instead.
  3. It doesn't wait for the write pumps to complete.
  4. It doesn't hit the admin or leader websocket, I think.
  5. I've never used this websocket library before, but I don't think users get 1001 when the server shuts down. I'll need to double-check this.

@karyao

karyao commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

closing this bc im a dumbass

@karyao karyao closed this Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Player session never recovers after server restart

2 participants