-
Notifications
You must be signed in to change notification settings - Fork 204
Restart lobby resurrection #376
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: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package api | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "encoding/base64" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| json "encoding/json" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "errors" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "fmt" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -63,7 +64,6 @@ type LobbyEntry struct { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (handler *V1Handler) getLobbies(writer http.ResponseWriter, _ *http.Request) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // REMARK: If paging is ever implemented, we might want to maintain order | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // when deleting lobbies from state in the state package. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lobbies := state.GetPublicLobbies() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lobbyEntries := make(LobbyEntries, 0, len(lobbies)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, lobby := range lobbies { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -80,7 +80,7 @@ func (handler *V1Handler) getLobbies(writer http.ResponseWriter, _ *http.Request | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MaxClientsPerIP: lobby.ClientsPerIPLimit, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Wordpack: lobby.Wordpack, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| State: lobby.State, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Scoring: lobby.ScoreCalculation.Identifier(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Scoring: lobby.ScoreCalculationIdentifier, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -92,6 +92,28 @@ func (handler *V1Handler) getLobbies(writer http.ResponseWriter, _ *http.Request | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (handler *V1Handler) resurrectLobby(writer http.ResponseWriter, request *http.Request) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var data game.LobbyRestoreData | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| base64Decoder := base64.NewDecoder(base64.StdEncoding, request.Body) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := json.NewDecoder(base64Decoder).Decode(&data); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.Println("Error unmarshalling lobby resurrection data:", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lobby := data.Lobby | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // We add the lobby, while the lobby mutex is aqcuired. This prevents us | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // from attempting to connect to the lobby, before the internal state has | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // been restored correctly. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lobby.Synchronized(func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if state.ResurrectLobby(lobby) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lobby.WriteObject = WriteObject | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lobby.WritePreparedMessage = WritePreparedMessage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lobby.ResurrectUnsynchronized(&data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+95
to
+112
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. Guard against nil lobby payloads before calling methods. If 🐛 Proposed fix lobby := data.Lobby
+ if lobby == nil {
+ http.Error(writer, "missing lobby payload", http.StatusBadRequest)
+ return
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents🧩 Analysis chain🏁 Script executed: rg -n -C2 "Resurrect|LobbyRestoreData|ShutdownTime|signature|HMAC|jwt|token|verify" internalRepository: scribble-rs/scribble.rs Length of output: 3174 🏁 Script executed: rg -n "resurrectLobby|/v1/lobby/resurrect" internal/apiRepository: scribble-rs/scribble.rs Length of output: 285 🏁 Script executed: cat internal/api/http.go | head -100Repository: scribble-rs/scribble.rs Length of output: 3716 🏁 Script executed: rg -n "SetupRoutes" --type go internalRepository: scribble-rs/scribble.rs Length of output: 690 🏁 Script executed: fd -e "*.go" -path "*/cmd/*" -o -path "*/main*" | head -10Repository: scribble-rs/scribble.rs Length of output: 235 🏁 Script executed: rg -n "\.SetupRoutes\|http\.HandleFunc\|http\.Handle" --type go | head -20Repository: scribble-rs/scribble.rs Length of output: 49 🏁 Script executed: find . -name "main.go" -o -name "*main*" | grep -v node_modules | head -5Repository: scribble-rs/scribble.rs Length of output: 212 🏁 Script executed: rg -n "SetupRoutes" --type-list | head -5Repository: scribble-rs/scribble.rs Length of output: 456 🏁 Script executed: git ls-files | grep -E "(main|cmd)" | head -10Repository: scribble-rs/scribble.rs Length of output: 202 🏁 Script executed: cat cmd/scribblers/main.go | head -200Repository: scribble-rs/scribble.rs Length of output: 2859 Validate cryptographic signature and TTL before accepting resurrection payloads. The endpoint accepts base64-decoded JSON payloads without any cryptographic verification. The Add HMAC or signature validation to the payload and verify it before calling 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (handler *V1Handler) postLobby(writer http.ResponseWriter, request *http.Request) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := request.ParseForm(); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| http.Error(writer, err.Error(), http.StatusBadRequest) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -51,7 +51,12 @@ func (handler *V1Handler) websocketUpgrade(writer http.ResponseWriter, request * | |||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| lobby := state.GetLobby(lobbyId) | ||||||||||||||||||||||||||||||||||||||||||||
| if lobby == nil { | ||||||||||||||||||||||||||||||||||||||||||||
| http.Error(writer, ErrLobbyNotExistent.Error(), http.StatusNotFound) | ||||||||||||||||||||||||||||||||||||||||||||
| socket, err := upgrader.Upgrade(writer, request) | ||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||
| http.Error(writer, err.Error(), http.StatusInternalServerError) | ||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| socket.WriteClose(1000, []byte("lobby_gone")) | ||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
53
to
61
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. Handle When the lobby is not found, the code upgrades the connection and writes a close frame, but:
Consider logging errors and ensuring the socket is closed. 🛡️ Proposed fix if lobby == nil {
socket, err := upgrader.Upgrade(writer, request)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
- socket.WriteClose(1000, []byte("lobby_gone"))
+ if err := socket.WriteClose(1000, []byte("lobby_gone")); err != nil {
+ log.Printf("error writing close frame: %v", err)
+ }
return
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,46 @@ | ||||||||||||||
| const discordInstanceId = getCookie("discord-instance-id") | ||||||||||||||
| const rootPath = `${discordInstanceId ? ".proxy/" : ""}{{.RootPath}}` | ||||||||||||||
|
|
||||||||||||||
| function createLabel(cssClass, forElement, text) { | ||||||||||||||
| const label = document.createElement("label"); | ||||||||||||||
| label.setAttribute("for", forElement); | ||||||||||||||
| label.classList.add(cssClass); | ||||||||||||||
| label.innerText = text; | ||||||||||||||
| return label; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| function createNumberInput(id, min, max, value) { | ||||||||||||||
| const input = document.createElement("input"); | ||||||||||||||
| input.setAttribute("type", "number"); | ||||||||||||||
| // Figure out why I did this exactly. | ||||||||||||||
| input.setAttribute("size", "4"); | ||||||||||||||
|
Comment on lines
+15
to
+16
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. 🧹 Nitpick | 🔵 Trivial Consider removing or addressing the TODO comment. The comment "Figure out why I did this exactly" suggests uncertainty about the 🤖 Prompt for AI Agents |
||||||||||||||
| input.setAttribute("id", id); | ||||||||||||||
| input.setAttribute("name", id); | ||||||||||||||
| input.setAttribute("value", Number.toString(value)); | ||||||||||||||
| input.setAttribute("min", Number.toString(min)); | ||||||||||||||
| input.setAttribute("max", Number.toString(max)); | ||||||||||||||
|
Comment on lines
+19
to
+21
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.
🐛 Proposed fix- input.setAttribute("value", Number.toString(value));
- input.setAttribute("min", Number.toString(min));
- input.setAttribute("max", Number.toString(max));
+ input.setAttribute("value", String(value));
+ input.setAttribute("min", String(min));
+ input.setAttribute("max", String(max));Alternatively, use 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
|
|
||||||||||||||
| const decButton = document.createElement("button"); | ||||||||||||||
| decButton.setAttribute("type", "button"); | ||||||||||||||
| decButton.classList.add("number-decrement"); | ||||||||||||||
| decButton.addEventListener("click", function() { | ||||||||||||||
| input.stepDown(); | ||||||||||||||
| }) | ||||||||||||||
| decButton.innerText = "-"; | ||||||||||||||
|
|
||||||||||||||
| const incButton = document.createElement("button"); | ||||||||||||||
| incButton.setAttribute("type", "button"); | ||||||||||||||
| incButton.classList.add("number-increment"); | ||||||||||||||
| incButton.addEventListener("click", function() { | ||||||||||||||
| input.stepUp(); | ||||||||||||||
| }) | ||||||||||||||
| incButton.innerText = "+"; | ||||||||||||||
|
|
||||||||||||||
| const div = document.createElement("div") | ||||||||||||||
| div.append(decButton, input, incButton); | ||||||||||||||
| return div; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| Array | ||||||||||||||
| .from(document.getElementsByClassName("number-input")) | ||||||||||||||
| .forEach(number_input => { | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,41 +10,73 @@ let hasSocketEverConnected = false; | |||||||||||||||||||||||||||||||||
| let socket; | ||||||||||||||||||||||||||||||||||
| function connectToWebsocket() { | ||||||||||||||||||||||||||||||||||
| if (socketIsConnecting === true) { | ||||||||||||||||||||||||||||||||||
| console.log("aborting connection attempt."); | ||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| socketIsConnecting = true; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| socket = new WebSocket(`${rootPath}/v1/lobby/ws`); | ||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||
| socket = new WebSocket(`${rootPath}/v1/lobby/ws`); | ||||||||||||||||||||||||||||||||||
| } catch (exception) { | ||||||||||||||||||||||||||||||||||
| console.log("Connection error:" + exception) | ||||||||||||||||||||||||||||||||||
| socketIsConnecting = false; | ||||||||||||||||||||||||||||||||||
| connectToWebsocket(); | ||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| socket.onerror = error => { | ||||||||||||||||||||||||||||||||||
| //Is not connected and we haven't yet said that we are done trying to | ||||||||||||||||||||||||||||||||||
| //connect, this means that we could never even establish a connection. | ||||||||||||||||||||||||||||||||||
| if (socket.readyState != 1 && !hasSocketEverConnected) { | ||||||||||||||||||||||||||||||||||
| if (socket.readyState != 1) { | ||||||||||||||||||||||||||||||||||
| socketIsConnecting = false; | ||||||||||||||||||||||||||||||||||
| showTextDialog("connection-error-dialog", | ||||||||||||||||||||||||||||||||||
| '{{.Translation.Get "error-connecting"}}', | ||||||||||||||||||||||||||||||||||
| `{{.Translation.Get "error-connecting-text"}}`); | ||||||||||||||||||||||||||||||||||
| console.log("Error establishing connection: ", error); | ||||||||||||||||||||||||||||||||||
| if (!hasSocketEverConnected) { | ||||||||||||||||||||||||||||||||||
| showTextDialog("connection-error-dialog", | ||||||||||||||||||||||||||||||||||
| '{{.Translation.Get "error-connecting"}}', | ||||||||||||||||||||||||||||||||||
| `{{.Translation.Get "error-connecting-text"}}`); | ||||||||||||||||||||||||||||||||||
| console.log("Error establishing connection: ", error); | ||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||
| connectToWebsocket(); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+19
to
+40
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. Avoid immediate recursive reconnects; schedule retries instead. Calling 🛠️ Proposed fix } catch (exception) {
console.log("Connection error:" + exception)
socketIsConnecting = false;
- connectToWebsocket();
+ setTimeout(connectToWebsocket, 1000);
return;
}
...
if (!hasSocketEverConnected) {
showTextDialog("connection-error-dialog",
'{{.Translation.Get "error-connecting"}}',
`{{.Translation.Get "error-connecting-text"}}`);
console.log("Error establishing connection: ", error);
} else {
- connectToWebsocket();
+ setTimeout(connectToWebsocket, 1000);
}🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||
| console.log("Socket error: ", error) | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| socket.onopen = () => { | ||||||||||||||||||||||||||||||||||
| closeDialog(shutdownDialogId); | ||||||||||||||||||||||||||||||||||
| closeDialog(reconnectDialogId); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| hasSocketEverConnected = true; | ||||||||||||||||||||||||||||||||||
| socketIsConnecting = false; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| socket.onclose = event => { | ||||||||||||||||||||||||||||||||||
| //We want to avoid handling the error multiple times and showing the incorrect dialogs. | ||||||||||||||||||||||||||||||||||
| //We w to avoid handling the error multiple times and showing the incorrect dialogs. | ||||||||||||||||||||||||||||||||||
| socket.onerror = null; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| console.log("Socket Closed Connection: ", event); | ||||||||||||||||||||||||||||||||||
| console.log("Attempting to reestablish socket connection."); | ||||||||||||||||||||||||||||||||||
| showReconnectDialogIfNotShown(); | ||||||||||||||||||||||||||||||||||
| connectToWebsocket(); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if (restoreData && event.reason === "lobby_gone") { | ||||||||||||||||||||||||||||||||||
| console.log("Resurrecting lobby ...",); | ||||||||||||||||||||||||||||||||||
| fetch('/v1/lobby/resurrect', { | ||||||||||||||||||||||||||||||||||
| method: 'POST', | ||||||||||||||||||||||||||||||||||
| body: restoreData, | ||||||||||||||||||||||||||||||||||
| }).then(() => { | ||||||||||||||||||||||||||||||||||
| console.log("Attempting to reestablish socket connection after resurrection ..."); | ||||||||||||||||||||||||||||||||||
| socketIsConnecting = false; | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+59
to
+66
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. Use rootPath for resurrect endpoint to support proxy/root-path deployments. The fetch currently uses an absolute 🛠️ Proposed fix- fetch('/v1/lobby/resurrect', {
+ fetch(`${rootPath}/v1/lobby/resurrect`, {
method: 'POST',
body: restoreData,
}).then(() => {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| connectToWebsocket(); | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if (event.reason !== "lobby_gone" && event.reason !== "server_restart") { | ||||||||||||||||||||||||||||||||||
| console.log("Attempting to reestablish socket connection."); | ||||||||||||||||||||||||||||||||||
| showReconnectDialogIfNotShown(); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| if (event.reason === "server_restart") { | ||||||||||||||||||||||||||||||||||
| connectToWebsocket(); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| registerMessageHandler(socket); | ||||||||||||||||||||||||||||||||||
|
|
@@ -53,6 +85,7 @@ function connectToWebsocket() { | |||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const shutdownDialogId = "shutdown-dialog"; | ||||||||||||||||||||||||||||||||||
| const reconnectDialogId = "reconnect-dialog"; | ||||||||||||||||||||||||||||||||||
| function showReconnectDialogIfNotShown() { | ||||||||||||||||||||||||||||||||||
| const previousReconnectDialog = document.getElementById(reconnectDialogId); | ||||||||||||||||||||||||||||||||||
|
|
@@ -833,6 +866,7 @@ let rounds = 0; | |||||||||||||||||||||||||||||||||
| let roundEndTime = 0; | ||||||||||||||||||||||||||||||||||
| let gameState = "unstarted"; | ||||||||||||||||||||||||||||||||||
| let drawingTimeSetting = "∞"; | ||||||||||||||||||||||||||||||||||
| let restoreData; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| function registerMessageHandler(targetSocket) { | ||||||||||||||||||||||||||||||||||
| targetSocket.onmessage = event => { | ||||||||||||||||||||||||||||||||||
|
|
@@ -985,10 +1019,16 @@ function registerMessageHandler(targetSocket) { | |||||||||||||||||||||||||||||||||
| + '{{.Translation.Get "custom-words-per-turn-setting"}}: ' + parsed.data.customWordsPerTurn + "%\n" | ||||||||||||||||||||||||||||||||||
| + '{{.Translation.Get "players-per-ip-limit-setting"}}: ' + parsed.data.clientsPerIpLimit); | ||||||||||||||||||||||||||||||||||
| } else if (parsed.type === "shutdown") { | ||||||||||||||||||||||||||||||||||
| socket.onclose = null; | ||||||||||||||||||||||||||||||||||
| socket.close(); | ||||||||||||||||||||||||||||||||||
| showDialog("shutdown-info", "Server shutting down", | ||||||||||||||||||||||||||||||||||
| document.createTextNode("Sorry, but the server is about to shut down. Please come back at a later time.")); | ||||||||||||||||||||||||||||||||||
| console.log("Shutdown event received"); | ||||||||||||||||||||||||||||||||||
| if (parsed.data) { | ||||||||||||||||||||||||||||||||||
| restoreData = parsed.data; | ||||||||||||||||||||||||||||||||||
| // FIXMe Text anpassen! | ||||||||||||||||||||||||||||||||||
| showDialog("shutdown-dialog", "Server shutting down", | ||||||||||||||||||||||||||||||||||
| document.createTextNode("Sorry, but the server is about to shut down. Attempting to restore lobby on restart ...")); | ||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||
| showDialog("shutdown-dialog", "Server shutting down", | ||||||||||||||||||||||||||||||||||
| document.createTextNode("Sorry, but the server is about to shut down. Please come back at a later time.")); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+1022
to
+1031
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. Resolve the FIXME and localize shutdown messaging. There’s a If you want, I can draft localized strings and wire them into the translation map. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
|
|
@@ -1033,6 +1073,7 @@ function setRoundTimeLeft(timeLeftMs) { | |||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| function handleReadyEvent(ready) { | ||||||||||||||||||||||||||||||||||
| restoreData = null; | ||||||||||||||||||||||||||||||||||
| ownerID = ready.ownerId; | ||||||||||||||||||||||||||||||||||
| ownID = ready.playerId; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
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.
Limit resurrection payload size to prevent memory abuse.
The handler currently reads an unbounded request body through base64+JSON decoding. Cap the size to avoid memory exhaustion and return a 4xx on invalid payloads.
🛡️ Proposed fix
func (handler *V1Handler) resurrectLobby(writer http.ResponseWriter, request *http.Request) { var data game.LobbyRestoreData + const maxRestorePayloadBytes = 1 << 20 // 1 MiB (tune as needed) + request.Body = http.MaxBytesReader(writer, request.Body, maxRestorePayloadBytes) base64Decoder := base64.NewDecoder(base64.StdEncoding, request.Body) if err := json.NewDecoder(base64Decoder).Decode(&data); err != nil { log.Println("Error unmarshalling lobby resurrection data:", err) - http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + http.Error(writer, "invalid resurrection payload", http.StatusBadRequest) return }📝 Committable suggestion
🤖 Prompt for AI Agents