Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions internal/api/v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ func (handler *V1Handler) getLobbies(writer http.ResponseWriter, _ *http.Request
}

func (handler *V1Handler) resurrectLobby(writer http.ResponseWriter, request *http.Request) {
// Limit request body size to 10MB to prevent memory exhaustion
request.Body = http.MaxBytesReader(writer, request.Body, 10*1024*1024)

var data game.LobbyRestoreData
base64Decoder := base64.NewDecoder(base64.StdEncoding, request.Body)
if err := json.NewDecoder(base64Decoder).Decode(&data); err != nil {
Expand All @@ -102,16 +105,35 @@ func (handler *V1Handler) resurrectLobby(writer http.ResponseWriter, request *ht
}

lobby := data.Lobby
// We add the lobby, while the lobby mutex is aqcuired. This prevents us
if lobby == nil {
http.Error(writer, "invalid lobby data", http.StatusBadRequest)
return
}

// Basic validation of lobby data
if lobby.LobbyID == "" {
http.Error(writer, "invalid lobby ID", http.StatusBadRequest)
return
}

// We add the lobby, while the lobby mutex is acquired. This prevents us
// from attempting to connect to the lobby, before the internal state has
// been restored correctly.
var resurrected bool
lobby.Synchronized(func() {
if state.ResurrectLobby(lobby) {
resurrected = state.ResurrectLobby(lobby)
if resurrected {
lobby.WriteObject = WriteObject
lobby.WritePreparedMessage = WritePreparedMessage
lobby.ResurrectUnsynchronized(&data)
}
})

if resurrected {
writer.WriteHeader(http.StatusNoContent)
} else {
http.Error(writer, "lobby already exists", http.StatusConflict)
}
}

func (handler *V1Handler) postLobby(writer http.ResponseWriter, request *http.Request) {
Expand Down
28 changes: 21 additions & 7 deletions internal/frontend/lobby.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,29 @@ function connectToWebsocket() {
console.log("Socket Closed Connection: ", event);

if (restoreData && event.reason === "lobby_gone") {
console.log("Resurrecting lobby ...",);
fetch('/v1/lobby/resurrect', {
console.log("Resurrecting lobby ...");
fetch(`${rootPath}/v1/lobby/resurrect`, {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
body: restoreData,
}).then(() => {
console.log("Attempting to reestablish socket connection after resurrection ...");
socketIsConnecting = false;
connectToWebsocket();
}).then(response => {
if (response.ok) {
console.log("Attempting to reestablish socket connection after resurrection ...");
socketIsConnecting = false;
connectToWebsocket();
} else {
console.error("Failed to resurrect lobby:", response.statusText);
showTextDialog("connection-error-dialog",
'{{.Translation.Get "error-connecting"}}',
'Failed to restore lobby. Please try again later.');
}
}).catch(error => {
console.error("Error resurrecting lobby:", error);
showTextDialog("connection-error-dialog",
'{{.Translation.Get "error-connecting"}}',
'Failed to restore lobby. Please try again later.');
});

return
Expand Down Expand Up @@ -1022,7 +1037,6 @@ function registerMessageHandler(targetSocket) {
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 {
Expand Down
4 changes: 2 additions & 2 deletions internal/game/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ type LobbyRestoreData struct {
func (lobby *Lobby) ResurrectUnsynchronized(restoreData *LobbyRestoreData) {
lobby.lowercaser = WordlistData[lobby.Wordpack].Lowercaser()

// Since we don't know how long the restart took, we extend all timers.\
// Since we don't know how long the restart took, we extend all timers.
// We add an additional second for good measure.
now := time.Now()
timeDiff := now.Sub(restoreData.ShutdownTime).Milliseconds() + 1000
Expand Down Expand Up @@ -166,7 +166,7 @@ const (

func (lobby *Lobby) GetPlayerByID(id uuid.UUID) *Player {
for _, player := range lobby.Players {
if player.ID == player.ID {
if player.ID == id {
return player
}
}
Expand Down
13 changes: 8 additions & 5 deletions internal/game/lobby.go
Original file line number Diff line number Diff line change
Expand Up @@ -1157,12 +1157,15 @@ func (lobby *Lobby) Shutdown() {
// Since broadcast is synchronous, we gotta use the asynchronous queue, to
// make sure the message is received before closing.
var waitGroup sync.WaitGroup
waitGroup.Add(len(lobby.Players))
for _, player := range lobby.Players {
player.ws.Async(func() {
defer waitGroup.Done()
player.ws.WriteClose(1012, []byte("server_restart"))
})
if player.ws != nil {
waitGroup.Add(1)
ws := player.ws
ws.Async(func() {
defer waitGroup.Done()
ws.WriteClose(1012, []byte("server_restart"))
})
}
}
waitGroup.Wait()
}
Expand Down
4 changes: 2 additions & 2 deletions internal/state/lobbies.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ func addLobby(lobby *game.Lobby) {
}

func ResurrectLobby(lobby *game.Lobby) bool {
globalStateMutex.RLock()
defer globalStateMutex.RUnlock()
globalStateMutex.Lock()
defer globalStateMutex.Unlock()

existingLobby := getLobby(lobby.LobbyID)
if existingLobby == nil {
Expand Down