From e92dc8ba36814a8daca82b4d366528e62f73a152 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Mon, 19 Aug 2024 14:52:38 -0700 Subject: [PATCH] show how many players in lobby --- TODO.txt | 5 +++-- src/client/Client.ts | 7 +++++++ src/client/ClientGame.ts | 13 ++++++++++++- src/client/index.html | 6 +++--- src/core/Schemas.ts | 13 +++++++++++-- src/server/GameServer.ts | 13 +++++++++++++ src/server/Server.ts | 2 +- update-deploy.sh | 8 +++++--- 8 files changed, 55 insertions(+), 12 deletions(-) diff --git a/TODO.txt b/TODO.txt index 8202355bb..5ede03dda 100644 --- a/TODO.txt +++ b/TODO.txt @@ -32,9 +32,10 @@ * use analyitics DONE 8/18/2024 * Use Overpass font DONE 8/18/2024 * BUG: invert zoom DONE 8/19/2024 -* better algorithm for name render placement -* show how many players in each lobby +* better algorithm for name render placement DONE 8/19/2024 +* show how many players in each lobby DONE 8/19/2024 * make boats larger +* boats same color as owner * make coasts look better * have boats not get close to shore * BUG: boat doesn't work if on lake on other player not on lake diff --git a/src/client/Client.ts b/src/client/Client.ts index ebdf8620a..08d96426e 100644 --- a/src/client/Client.ts +++ b/src/client/Client.ts @@ -69,6 +69,7 @@ class Client { const playerCountElement = document.createElement('div'); playerCountElement.className = 'player-count'; + playerCountElement.textContent = `Players: ${lobby.numClients}` button.appendChild(nameElement); button.appendChild(timerElement); @@ -121,6 +122,12 @@ class Client { } this.game = createClientGame(getUsername(), new PseudoRandom(Date.now()).nextID(), lobby.id, getConfig(), map); this.game.join(); + const g = this.game + window.addEventListener('beforeunload', function (event) { + // Your function logic here + console.log('Browser is closing'); + g.stop() + }); }); } } diff --git a/src/client/ClientGame.ts b/src/client/ClientGame.ts index 307e04c87..c547460d5 100644 --- a/src/client/ClientGame.ts +++ b/src/client/ClientGame.ts @@ -5,7 +5,7 @@ import {EventBus} from "../core/EventBus"; import {Config} from "../core/configuration/Config"; import {GameRenderer} from "./graphics/GameRenderer"; import {InputHandler, MouseUpEvent, ZoomEvent, DragEvent, MouseDownEvent} from "./InputHandler" -import {ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, ClientMessageSchema, GameID, Intent, ServerMessage, ServerMessageSchema, ServerSyncMessage, Turn} from "../core/Schemas"; +import {ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, ClientLeaveMessageSchema, ClientMessageSchema, GameID, Intent, ServerMessage, ServerMessageSchema, ServerSyncMessage, Turn} from "../core/Schemas"; @@ -123,6 +123,17 @@ export class ClientGame { public stop() { clearInterval(this.intervalID) this.isActive = false + if (this.socket.readyState === WebSocket.OPEN) { + const msg = ClientLeaveMessageSchema.parse({ + type: "leave", + clientID: this.id, + gameID: this.gameID, + }) + this.socket.send(JSON.stringify(msg)) + } else { + console.log('WebSocket is not open. Current state:', this.socket.readyState); + console.log('attempting reconnect') + } } public addTurn(turn: Turn): void { diff --git a/src/client/index.html b/src/client/index.html index 75cc20b5b..10fa9afcb 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -108,12 +108,12 @@ .player-count { font-size: 24px; - color: #aaddff; + color: #000000; } .joining-message { font-size: 48px; - color: white; + color: rgb(0, 0, 0); text-align: center; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); } @@ -121,7 +121,7 @@ h1 { font-family: 'Overpass', sans-serif; text-align: center; - color: #ffffff; + color: #000000; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); font-size: 2.5em; margin-bottom: 30px; diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index 01afded7a..06d81e86c 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -11,7 +11,7 @@ export type BoatAttackIntent = z.infer export type Turn = z.infer -export type ClientMessage = ClientIntentMessage | ClientJoinMessage +export type ClientMessage = ClientIntentMessage | ClientJoinMessage | ClientLeaveMessage export type ServerMessage = ServerSyncMessage | ServerStartGameMessage export type ServerSyncMessage = z.infer @@ -20,10 +20,13 @@ export type ServerStartGameMessage = z.infer export type ClientJoinMessage = z.infer +export type ClientLeaveMessage = z.infer + export interface Lobby { id: string; startTime: number; + numClients: number; } // Zod schemas @@ -109,4 +112,10 @@ export const ClientJoinMessageSchema = ClientBaseMessageSchema.extend({ lastTurn: z.number() }) -export const ClientMessageSchema = z.union([ClientIntentMessageSchema, ClientJoinMessageSchema]); \ No newline at end of file +export const ClientLeaveMessageSchema = ClientBaseMessageSchema.extend({ + type: z.literal('leave'), + clientID: z.string(), + gameID: z.string(), +}) + +export const ClientMessageSchema = z.union([ClientIntentMessageSchema, ClientJoinMessageSchema, ClientLeaveMessageSchema]); \ No newline at end of file diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index 6bb54c9f3..ebea84eeb 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -42,6 +42,15 @@ export class GameServer { console.warn(`client ${clientMsg.clientID} sent to wrong game`) } } + if (clientMsg.type == "leave") { + const toRemove = this.clients.filter(c => c.id) + if (toRemove.length == 0) { + return + } + toRemove[0].ws.close() + console.log(`client ${toRemove[0].id} left game`) + this.clients = this.clients.filter(c => c.id != clientMsg.clientID) + } }) // In case a client joined the game late and missed the start message. @@ -50,6 +59,10 @@ export class GameServer { } } + public numClients(): number { + return this.clients.length + } + public startTime(): number { return this.createdAt + this.config.lobbyLifetime() } diff --git a/src/server/Server.ts b/src/server/Server.ts index 45df25710..f6540674f 100644 --- a/src/server/Server.ts +++ b/src/server/Server.ts @@ -26,7 +26,7 @@ const gm = new GameManager(getConfig()) // New GET endpoint to list lobbies app.get('/lobbies', (req, res) => { res.json({ - lobbies: gm.gamesByPhase(GamePhase.Lobby).map(g => ({id: g.id, startTime: g.startTime()})), + lobbies: gm.gamesByPhase(GamePhase.Lobby).map(g => ({id: g.id, startTime: g.startTime(), numClients: g.numClients()})), }); }); diff --git a/update-deploy.sh b/update-deploy.sh index afe8b5656..33000e6a9 100755 --- a/update-deploy.sh +++ b/update-deploy.sh @@ -18,9 +18,11 @@ fi # Set the instance name based on the environment if [[ "$ENV" == "dev" ]]; then INSTANCE_NAME="openfrontio-dev-instance" + TAG="dev" echo "[DEV] Deploying to openfront.dev" else INSTANCE_NAME="openfrontio-instance" + TAG="latest" echo "[PROD] Deploying to openfront.io" fi @@ -31,17 +33,17 @@ gcloud auth configure-docker us-central1-docker.pkg.dev docker build -t openfrontio . # Tag the new image (use a version number or 'latest') -docker tag openfrontio us-central1-docker.pkg.dev/openfrontio/openfrontio/game-server:$ENV +docker tag openfrontio us-central1-docker.pkg.dev/openfrontio/openfrontio/game-server:$TAG # Push the new image to Google Container Registry -docker push us-central1-docker.pkg.dev/openfrontio/openfrontio/game-server:$ENV +docker push us-central1-docker.pkg.dev/openfrontio/openfrontio/game-server:$TAG # Prune Docker system on the instance gcloud compute ssh $INSTANCE_NAME --zone us-central1-a --command 'docker system prune -f -a' # Update the GCE instance with the new container image gcloud compute instances update-container $INSTANCE_NAME \ - --container-image us-central1-docker.pkg.dev/openfrontio/openfrontio/game-server:latest \ + --container-image us-central1-docker.pkg.dev/openfrontio/openfrontio/game-server:$TAG \ --zone=us-central1-a echo "Deployment to $ENV environment complete. New version should be live soon on $INSTANCE_NAME." \ No newline at end of file