diff --git a/resources/lang/en.json b/resources/lang/en.json index 2fbb59b57..8e7bf662d 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -863,7 +863,8 @@ }, "kick_reason": { "duplicate_session": "Kicked from game (you may have been playing on another tab)", - "lobby_creator": "Kicked by lobby creator" + "lobby_creator": "Kicked by lobby creator", + "host_left": "The host has left the lobby." }, "send_troops_modal": { "title_with_name": "Send Troops to {name}", diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index 55946dce0..aefbac8ed 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -172,6 +172,15 @@ export function joinLobby( composed: true, }), ); + } else if (message.error === "kick_reason.host_left") { + alert(translateText("kick_reason.host_left")); + document.dispatchEvent( + new CustomEvent("leave-lobby", { + detail: { lobby: lobbyConfig.gameID, cause: "host-left" }, + bubbles: true, + composed: true, + }), + ); } else { showErrorModal( message.error, diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index da724214c..f6ac05e76 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -35,6 +35,7 @@ export enum GamePhase { const KICK_REASON_DUPLICATE_SESSION = "kick_reason.duplicate_session"; const KICK_REASON_LOBBY_CREATOR = "kick_reason.lobby_creator"; +const KICK_REASON_HOST_LEFT = "kick_reason.host_left"; const KICK_REASON_TOO_MUCH_DATA = "kick_reason.too_much_data"; const KICK_REASON_INVALID_MESSAGE = "kick_reason.invalid_message"; @@ -542,6 +543,20 @@ export class GameServer { this.activeClients = this.activeClients.filter( (c) => c.clientID !== client.clientID, ); + // Close lobby when host leaves before game starts + if ( + !this._hasStarted && + !this.isPublic() && + client.persistentID === this.creatorPersistentID + ) { + this.log.info("Host left, closing lobby", { + gameID: this.id, + }); + for (const c of [...this.activeClients]) { + this.kickClient(c.clientID, KICK_REASON_HOST_LEFT); + } + this._hasEnded = true; + } }); client.ws.on("error", (error: Error) => { if ((error as any).code === "WS_ERR_UNEXPECTED_RSV_1") { @@ -847,6 +862,8 @@ export class GameServer { } else { return GamePhase.Active; } + } else if (this._hasEnded) { + return GamePhase.Finished; } else { return GamePhase.Lobby; }