Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 0
Fix: player session never recovers when server resets#18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base:main
Are you sure you want to change the base?
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
1285d747a8c616fe5a0369e60e1420cc73bf72fbea9093e93ecc9e08bc62000461f1032d9edf5f4b375b6c0f83cFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -23,6 +23,12 @@ type Hub struct { | ||
| inform chan PlayerID | ||
| state chan GameState | ||
| clearOffline chan chan struct{} | ||
| shutdown chan shutdownEvent | ||
| } | ||
| type shutdownEvent struct { | ||
| command string | ||
| done chan struct{} | ||
| } | ||
| func NewHub(players *Players, games ...*Game) *Hub { | ||
| @@ -38,6 +44,7 @@ func NewHub(players *Players, games ...*Game) *Hub { | ||
| inform: make(chan PlayerID), | ||
| state: make(chan GameState), | ||
| clearOffline: make(chan chan struct{}), | ||
| shutdown: make(chan shutdownEvent), | ||
| } | ||
| if len(games) > 0 { | ||
| hub.game = games[0] | ||
| @@ -61,6 +68,9 @@ func (h *Hub) Run() { | ||
| case done := <-h.clearOffline: | ||
| h.clearOfflineLocations() | ||
| close(done) | ||
| case event := <-h.shutdown: | ||
| h.broadcastShutDown(event.command) | ||
| close(event.done) | ||
| } | ||
| } | ||
| } | ||
| @@ -178,7 +188,7 @@ func (h *Hub) unregisterConnection(connection *Conn) { | ||
| if hasCoordinate && isMapVisibleRole(player.Type) { | ||
| message, ok := informMessage(player, coordinate) | ||
| if ok { | ||
| h.broadcast(message, nil, onlyViewers) | ||
| h.broadcastControl(message, nil, onlyViewers) | ||
| } | ||
| } else { | ||
| h.broadcastRemove(connection.playerID, onlyViewers, nil) | ||
| @@ -293,15 +303,46 @@ func (h *Hub) broadcast(message []byte, origin *Conn, include connectionFilter) | ||
| } | ||
| } | ||
| func (h *Hub) broadcastControl(message []byte, origin *Conn, include connectionFilter) { | ||
| var failedConnections []*Conn | ||
| for connection := range h.connections { | ||
| if connection == origin || !include(connection) { | ||
| continue | ||
| } | ||
| if !h.enqueueControl(connection, message) { | ||
| failedConnections = append(failedConnections, connection) | ||
| } | ||
| } | ||
| for _, connection := range failedConnections { | ||
| h.unregisterConnection(connection) | ||
| } | ||
| } | ||
| func (h *Hub) enqueue(connection *Conn, message []byte) bool { | ||
| select { | ||
| case connection.send <- message: | ||
| return true | ||
| default: | ||
| // Channel buffer is full. Client cannot keep up with real-time updates. | ||
| return false | ||
| } | ||
| } | ||
| func (h *Hub) enqueueControl(connection *Conn, message []byte) bool { | ||
| if h.enqueue(connection, message) { | ||
| return true | ||
| } | ||
| for { | ||
| select { | ||
| case <-connection.send: | ||
| // Drain older queued messages until the buffer is empty to prioritize this critical control message. | ||
| default: | ||
| // Buffer is now completely drained. Enqueue the critical control message. | ||
| return h.enqueue(connection, message) | ||
| } | ||
| } | ||
| } | ||
| func informMessage(player PlayerResponse, coordinate Coordinate) ([]byte, bool) { | ||
| playerJSON, err := json.Marshal(player) | ||
| if err != nil { | ||
| @@ -351,7 +392,7 @@ func (h *Hub) broadcastInform(playerID PlayerID, origin *Conn) { | ||
| if h.connectionCanSee(connection, playerID, player.Type) { | ||
| outgoing = message | ||
| } | ||
| if !h.enqueue(connection, outgoing) { | ||
| if !h.enqueueControl(connection, outgoing) { | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above, other messages can still be relevant. | ||
| slowConnections = append(slowConnections, connection) | ||
| } | ||
| } | ||
| @@ -367,7 +408,7 @@ func (h *Hub) broadcastInform(playerID PlayerID, origin *Conn) { | ||
| if retained && isMapVisibleRole(player.Type) { | ||
| message, ok := informMessage(player, coordinate) | ||
| if ok { | ||
| h.broadcast(message, nil, onlyViewers) | ||
| h.broadcastControl(message, nil, onlyViewers) | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above, other messages can still be relevant. | ||
| } | ||
| return | ||
| } | ||
| @@ -411,7 +452,7 @@ func removeMessage(playerID PlayerID) []byte { | ||
| } | ||
| func (h *Hub) broadcastRemove(playerID PlayerID, include connectionFilter, origin *Conn) { | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above, other messages can still be relevant. This could leave stale markers and only the final one is accepted. | ||
| h.broadcast(removeMessage(playerID), origin, include) | ||
| h.broadcastControl(removeMessage(playerID), origin, include) | ||
| } | ||
| func (h *Hub) clearOfflineLocations() { | ||
| @@ -420,3 +461,15 @@ func (h *Hub) clearOfflineLocations() { | ||
| h.broadcastRemove(playerID, onlyViewers, nil) | ||
| } | ||
| } | ||
| func (h *Hub) broadcastShutDown(command string) { | ||
| message, err := json.Marshal(Message{Command: command}) | ||
| if err != nil { | ||
| return | ||
| } | ||
| for connection := range h.connections { | ||
| if !h.enqueueControl(connection, message) { | ||
| h.unregisterConnection(connection) | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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.