mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:50:43 +00:00
render player names efficiently
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
* fix conquer expansion DONE
|
||||
* perf improvements on graphics (only draw images to canvas on ticks) DONE
|
||||
* double join lobby bug
|
||||
* double join lobby bug DONE
|
||||
* render player info efficiently
|
||||
* better troop addition logic
|
||||
* use draw rect instead of image data ?
|
||||
* better expansion, add back directed expansion
|
||||
* use pastel theme for territories
|
||||
* improve front page
|
||||
* add username in front page
|
||||
* upload and start server
|
||||
* make boats larger
|
||||
* have boats not get close to shore
|
||||
* better algorithm for name render placement
|
||||
* re-enable directed expansion
|
||||
* better algorithm for name render placement
|
||||
@@ -11,7 +11,6 @@ import {v4 as uuidv4} from 'uuid';
|
||||
class Client {
|
||||
private hasJoined = false
|
||||
|
||||
private startButton: HTMLButtonElement | null;
|
||||
private socket: WebSocket | null = null;
|
||||
private terrainMap: Promise<TerrainMap>
|
||||
private game: ClientGame
|
||||
@@ -20,7 +19,6 @@ class Client {
|
||||
private lobbiesInterval: NodeJS.Timeout | null = null;
|
||||
|
||||
constructor() {
|
||||
this.startButton = document.getElementById('startButton') as HTMLButtonElement | null;
|
||||
this.lobbiesContainer = document.getElementById('lobbies-container');
|
||||
|
||||
}
|
||||
@@ -80,7 +78,12 @@ class Client {
|
||||
}
|
||||
|
||||
private async joinLobby(lobbyID: string) {
|
||||
clearInterval(this.lobbiesInterval)
|
||||
this.lobbiesContainer.innerHTML = 'Joining'; // Clear existing lobbies
|
||||
this.terrainMap.then((map) => {
|
||||
if (this.game != null) {
|
||||
return
|
||||
}
|
||||
this.game = createClientGame(uuidv4().slice(0, 4), generateUniqueID(), lobbyID, defaultSettings, map)
|
||||
this.game.joinLobby()
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ import {createGame} from "../core/GameImpl";
|
||||
import {Ticker, TickEvent} from "../core/Ticker";
|
||||
import {EventBus} from "../core/EventBus";
|
||||
import {Settings} from "../core/Settings";
|
||||
import {GameRenderer} from "./GameRenderer";
|
||||
import {GameRenderer} from "./graphics/GameRenderer";
|
||||
import {InputHandler, MouseUpEvent, ZoomEvent, DragEvent, MouseDownEvent} from "./InputHandler"
|
||||
import {ClientIntentMessageSchema, ClientJoinMessageSchema, ClientMessageSchema, ServerMessage, ServerMessageSchema, ServerSyncMessage, Turn} from "../core/Schemas";
|
||||
|
||||
@@ -34,7 +34,7 @@ export class ClientGame {
|
||||
private myPlayer: Player
|
||||
private turns: Turn[] = []
|
||||
private socket: WebSocket
|
||||
private started = false
|
||||
private isActive = false
|
||||
|
||||
private ticksPerTurn = 1
|
||||
|
||||
@@ -43,10 +43,12 @@ export class ClientGame {
|
||||
|
||||
private spawned = false
|
||||
|
||||
private intervalID: NodeJS.Timeout
|
||||
|
||||
constructor(
|
||||
private playerName: string,
|
||||
private id: ClientID,
|
||||
private lobbyID: LobbyID,
|
||||
private gameID: LobbyID,
|
||||
private ticker: Ticker,
|
||||
private eventBus: EventBus,
|
||||
private gs: Game,
|
||||
@@ -63,7 +65,7 @@ export class ClientGame {
|
||||
JSON.stringify(
|
||||
ClientJoinMessageSchema.parse({
|
||||
type: "join",
|
||||
lobbyID: this.lobbyID,
|
||||
lobbyID: this.gameID,
|
||||
clientID: this.id
|
||||
})
|
||||
)
|
||||
@@ -76,13 +78,14 @@ export class ClientGame {
|
||||
this.start()
|
||||
}
|
||||
if (message.type == "turn") {
|
||||
this.addTurn(message.turn)
|
||||
if (message.turn.intents)
|
||||
this.addTurn(message.turn)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public start() {
|
||||
this.started = true
|
||||
this.isActive = true
|
||||
console.log('starting game!')
|
||||
// TODO: make each class do this, or maybe have client intercept all requests?
|
||||
//this.eventBus.on(TickEvent, (e) => this.tick(e))
|
||||
@@ -98,7 +101,12 @@ export class ClientGame {
|
||||
this.executor.spawnBots(1000)
|
||||
|
||||
|
||||
setInterval(() => this.tick(), 10);
|
||||
this.intervalID = setInterval(() => this.tick(), 10);
|
||||
}
|
||||
|
||||
public stop() {
|
||||
clearInterval(this.intervalID)
|
||||
this.isActive = false
|
||||
}
|
||||
|
||||
public addTurn(turn: Turn): void {
|
||||
@@ -131,6 +139,9 @@ export class ClientGame {
|
||||
}
|
||||
|
||||
private inputEvent(event: MouseDownEvent) {
|
||||
if (!this.isActive) {
|
||||
return
|
||||
}
|
||||
const cell = this.renderer.screenToWorldCoordinates(event.x, event.y)
|
||||
if (!this.gs.isOnMap(cell)) {
|
||||
return
|
||||
@@ -164,6 +175,7 @@ export class ClientGame {
|
||||
ClientIntentMessageSchema.parse({
|
||||
type: "intent",
|
||||
clientID: this.id,
|
||||
gameID: this.gameID,
|
||||
intent: {
|
||||
type: "spawn",
|
||||
name: this.playerName,
|
||||
@@ -187,6 +199,7 @@ export class ClientGame {
|
||||
ClientIntentMessageSchema.parse({
|
||||
type: "intent",
|
||||
clientID: this.id,
|
||||
gameID: this.gameID,
|
||||
intent: {
|
||||
type: "attack",
|
||||
attackerID: this.myPlayer.id(),
|
||||
@@ -211,6 +224,7 @@ export class ClientGame {
|
||||
ClientIntentMessageSchema.parse({
|
||||
type: "intent",
|
||||
clientID: this.id,
|
||||
gameID: this.gameID,
|
||||
intent: {
|
||||
type: "boat",
|
||||
attackerID: this.myPlayer.id(),
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import {Colord} from "colord";
|
||||
import {Cell, MutableGame, Game, PlayerEvent, Tile, TileEvent, Player, Execution, BoatEvent} from "../core/Game";
|
||||
import {Theme} from "../core/Settings";
|
||||
import {DragEvent, ZoomEvent} from "./InputHandler";
|
||||
import {calculateBoundingBox, placeName} from "./NameBoxCalculator";
|
||||
import {PseudoRandom} from "../core/PseudoRandom";
|
||||
import {BoatAttackExecution} from "../core/execution/BoatAttackExecution";
|
||||
import {Cell, MutableGame, Game, PlayerEvent, Tile, TileEvent, Player, Execution, BoatEvent} from "../../core/Game";
|
||||
import {Theme} from "../../core/Settings";
|
||||
import {DragEvent, ZoomEvent} from "../InputHandler";
|
||||
import {calculateBoundingBox, placeName} from "../NameBoxCalculator";
|
||||
import {PseudoRandom} from "../../core/PseudoRandom";
|
||||
import {BoatAttackExecution} from "../../core/execution/BoatAttackExecution";
|
||||
import {NameRenderer} from "./NameRenderer";
|
||||
|
||||
|
||||
class NameRender {
|
||||
constructor(public lastRendered: number, public location: Cell, public fontSize: number) { }
|
||||
}
|
||||
|
||||
export class GameRenderer {
|
||||
private tempCanvas;
|
||||
@@ -21,16 +20,12 @@ export class GameRenderer {
|
||||
|
||||
private imageData: ImageData
|
||||
|
||||
private nameRenders: Map<Player, NameRender> = new Map()
|
||||
|
||||
private rand = new PseudoRandom(10)
|
||||
|
||||
private offscreenContext: CanvasRenderingContext2D
|
||||
private offscreenCanvas: HTMLCanvasElement
|
||||
private nameRenderer: NameRenderer;
|
||||
|
||||
|
||||
constructor(private gs: Game, private theme: Theme, private canvas: HTMLCanvasElement) {
|
||||
this.context = canvas.getContext("2d")
|
||||
this.nameRenderer = new NameRenderer(gs, theme)
|
||||
}
|
||||
|
||||
initialize() {
|
||||
@@ -46,6 +41,7 @@ export class GameRenderer {
|
||||
|
||||
this.imageData = this.context.getImageData(0, 0, this.gs.width(), this.gs.height())
|
||||
this.initImageData()
|
||||
this.nameRenderer.initialize()
|
||||
|
||||
|
||||
document.body.appendChild(this.canvas);
|
||||
@@ -53,12 +49,6 @@ export class GameRenderer {
|
||||
this.resizeCanvas();
|
||||
|
||||
|
||||
this.offscreenCanvas = document.createElement('canvas');
|
||||
this.offscreenContext = this.offscreenCanvas.getContext('2d');
|
||||
this.offscreenCanvas.width = this.gs.width();
|
||||
this.offscreenCanvas.height = this.gs.height();
|
||||
|
||||
|
||||
requestAnimationFrame(() => this.renderGame());
|
||||
}
|
||||
|
||||
@@ -111,14 +101,8 @@ export class GameRenderer {
|
||||
this.gs.height()
|
||||
);
|
||||
}
|
||||
|
||||
this.context.drawImage(
|
||||
this.offscreenCanvas,
|
||||
-this.gs.width() / 2,
|
||||
-this.gs.height() / 2,
|
||||
this.gs.width(),
|
||||
this.gs.height()
|
||||
);
|
||||
const [upperLeft, bottomRight] = this.boundingRect()
|
||||
this.nameRenderer.render(this.context, this.scale, upperLeft, bottomRight)
|
||||
|
||||
// const paths = this.gs.executions().map(e => e as Execution).filter(e => e instanceof BoatAttackExecution).map(e => e as BoatAttackExecution).filter(e => e.path != null).map(e => e.path)
|
||||
// paths.forEach(p => {
|
||||
@@ -139,56 +123,7 @@ export class GameRenderer {
|
||||
|
||||
// Put the ImageData on the temp canvas
|
||||
tempCtx.putImageData(this.imageData, 0, 0);
|
||||
let numCalcs = 0
|
||||
for (const player of this.gs.players()) {
|
||||
if (numCalcs < 50 && this.maybeRecalculatePlayerInfo(player)) {
|
||||
numCalcs++
|
||||
}
|
||||
//this.renderPlayerInfo(player)
|
||||
}
|
||||
}
|
||||
|
||||
maybeRecalculatePlayerInfo(player: Player): boolean {
|
||||
if (!this.nameRenders.has(player)) {
|
||||
this.nameRenders.set(player, new NameRender(0, null, null))
|
||||
}
|
||||
|
||||
const render = this.nameRenders.get(player)
|
||||
|
||||
let wasUpdated = false
|
||||
|
||||
if (Date.now() - render.lastRendered > 1000) {
|
||||
render.lastRendered = Date.now() + this.rand.nextInt(0, 100)
|
||||
wasUpdated = true
|
||||
|
||||
const box = calculateBoundingBox(player)
|
||||
const centerX = box.min.x + ((box.max.x - box.min.x) / 2)
|
||||
const centerY = box.min.y + ((box.max.y - box.min.y) / 2)
|
||||
render.location = new Cell(centerX, centerY)
|
||||
render.fontSize = Math.max(Math.min(box.max.x - box.min.x, box.max.y - box.min.y) / player.info().name.length / 2, 1)
|
||||
}
|
||||
return wasUpdated
|
||||
}
|
||||
|
||||
renderPlayerInfo(player: Player) {
|
||||
if (!player.isAlive()) {
|
||||
return
|
||||
}
|
||||
if (!this.nameRenders.has(player)) {
|
||||
return
|
||||
}
|
||||
|
||||
const render = this.nameRenders.get(player)
|
||||
|
||||
this.offscreenContext.font = `${render.fontSize}px Arial`;
|
||||
this.offscreenContext.fillStyle = this.theme.playerInfoColor(player.id()).toHex();
|
||||
this.offscreenContext.textAlign = 'center';
|
||||
this.offscreenContext.textBaseline = 'middle';
|
||||
|
||||
const nameCenterX = render.location.x - this.gs.width() / 2
|
||||
const nameCenterY = render.location.y - this.gs.height() / 2
|
||||
this.offscreenContext.fillText(player.info().name, nameCenterX, nameCenterY - render.fontSize / 2);
|
||||
this.offscreenContext.fillText(String(Math.floor(player.troops())), nameCenterX, nameCenterY + render.fontSize);
|
||||
this.nameRenderer.tick()
|
||||
}
|
||||
|
||||
tileUpdate(event: TileEvent) {
|
||||
@@ -271,12 +206,27 @@ export class GameRenderer {
|
||||
const gameX = centerX + this.gs.width() / 2
|
||||
const gameY = centerY + this.gs.height() / 2
|
||||
|
||||
|
||||
console.log(`zoom point ${centerX} ${centerY}`)
|
||||
console.log(`Current scale: ${this.scale}`);
|
||||
console.log(`Current offset: ${this.offsetX}, ${this.offsetY}`);
|
||||
|
||||
return new Cell(Math.floor(gameX), Math.floor(gameY));
|
||||
}
|
||||
|
||||
boundingRect(): [Cell, Cell] {
|
||||
|
||||
// Calculate the world point we want to zoom towards
|
||||
const LeftX = (- this.gs.width() / 2) / this.scale + this.offsetX;
|
||||
const TopY = (- this.gs.height() / 2) / this.scale + this.offsetY;
|
||||
|
||||
const gameLeftX = LeftX + this.gs.width() / 2
|
||||
const gameTopY = TopY + this.gs.height() / 2
|
||||
|
||||
|
||||
// Calculate the world point we want to zoom towards
|
||||
const rightX = (screen.width - this.gs.width() / 2) / this.scale + this.offsetX;
|
||||
const rightY = (screen.height - this.gs.height() / 2) / this.scale + this.offsetY;
|
||||
|
||||
const gameRightX = rightX + this.gs.width() / 2
|
||||
const gameBottomY = rightY + this.gs.height() / 2
|
||||
|
||||
return [new Cell(Math.floor(gameLeftX), Math.floor(gameTopY)), new Cell(Math.floor(gameRightX), Math.floor(gameBottomY))]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
import PriorityQueue from "priority-queue-typescript"
|
||||
import {Cell, Game, Player} from "../../core/Game"
|
||||
import {PseudoRandom} from "../../core/PseudoRandom"
|
||||
import {Theme} from "../../core/Settings"
|
||||
import {calculateBoundingBox} from "../NameBoxCalculator"
|
||||
|
||||
class RenderInfo {
|
||||
constructor(public player: Player, public lastRendered: number, public location: Cell, public fontSize: number) { }
|
||||
}
|
||||
|
||||
export class NameRenderer {
|
||||
|
||||
private lastChecked = 0
|
||||
private refreshRate = 1000
|
||||
|
||||
private rand = new PseudoRandom(10)
|
||||
private renderInfo: Map<Player, RenderInfo> = new Map()
|
||||
private context: CanvasRenderingContext2D
|
||||
private canvas: HTMLCanvasElement
|
||||
private toRender: PriorityQueue<RenderInfo> = new PriorityQueue<RenderInfo>(1000, (a: RenderInfo, b: RenderInfo) => a.lastRendered - b.lastRendered);
|
||||
private seenPlayers: Set<Player> = new Set()
|
||||
|
||||
|
||||
|
||||
constructor(private game: Game, private theme: Theme) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public initialize() {
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.context = this.canvas.getContext('2d');
|
||||
|
||||
this.canvas.style.position = 'fixed';
|
||||
this.canvas.style.left = '0';
|
||||
this.canvas.style.top = '0';
|
||||
this.canvas.width = this.game.width();
|
||||
this.canvas.height = this.game.height();
|
||||
}
|
||||
|
||||
public render(mainContex: CanvasRenderingContext2D, scale: number, uppperLeft: Cell, bottomRight: Cell) {
|
||||
// mainContex.drawImage(
|
||||
// this.canvas,
|
||||
// -this.game.width() / 2,
|
||||
// -this.game.height() / 2,
|
||||
// this.game.width(),
|
||||
// this.game.height()
|
||||
// )
|
||||
for (const render of this.toRender) {
|
||||
if (render.player.isAlive()) {
|
||||
this.renderPlayerInfo(render, mainContex, scale, uppperLeft, bottomRight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public tick() {
|
||||
const now = Date.now()
|
||||
if (now - this.lastChecked > this.refreshRate) {
|
||||
this.lastChecked = now
|
||||
for (const player of this.game.players()) {
|
||||
if (!this.seenPlayers.has(player)) {
|
||||
this.toRender.add(new RenderInfo(player, 0, null, null))
|
||||
this.seenPlayers.add(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (!this.toRender.empty() && now - this.toRender.peek().lastRendered > this.refreshRate) {
|
||||
const renderInfo = this.toRender.poll()
|
||||
this.calculateRenderInfo(renderInfo)
|
||||
renderInfo.lastRendered = now + this.rand.nextInt(-50, 50)
|
||||
this.toRender.add(renderInfo)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
calculateRenderInfo(render: RenderInfo): boolean {
|
||||
|
||||
let wasUpdated = false
|
||||
|
||||
render.lastRendered = Date.now() + this.rand.nextInt(0, 100)
|
||||
wasUpdated = true
|
||||
|
||||
const box = calculateBoundingBox(render.player)
|
||||
const centerX = box.min.x + ((box.max.x - box.min.x) / 2)
|
||||
const centerY = box.min.y + ((box.max.y - box.min.y) / 2)
|
||||
render.location = new Cell(centerX, centerY)
|
||||
render.fontSize = Math.max(Math.min(box.max.x - box.min.x, box.max.y - box.min.y) / render.player.info().name.length / 2, 2)
|
||||
return wasUpdated
|
||||
}
|
||||
|
||||
renderPlayerInfo(render: RenderInfo, context: CanvasRenderingContext2D, scale: number, uppperLeft: Cell, bottomRight: Cell) {
|
||||
|
||||
// console.log(`scale: ${scale}, fontSize: ${render.fontSize}, mult: ${scale * render.fontSize}`)
|
||||
if (render.fontSize * scale < 10) {
|
||||
return
|
||||
}
|
||||
|
||||
const nameCenterX = Math.floor(render.location.x - this.game.width() / 2)
|
||||
const nameCenterY = Math.floor(render.location.y - this.game.height() / 2)
|
||||
|
||||
if (render.location.x < uppperLeft.x || render.location.x > bottomRight.x || render.location.y < uppperLeft.y || render.location.y > bottomRight.y) {
|
||||
return
|
||||
}
|
||||
|
||||
// if (nameCenterX, ) {
|
||||
|
||||
// }
|
||||
|
||||
context.textRendering = "optimizeSpeed";
|
||||
|
||||
context.font = `${render.fontSize}px Arial`;
|
||||
context.fillStyle = this.theme.playerInfoColor(render.player.id()).toHex();
|
||||
context.textAlign = 'center';
|
||||
context.textBaseline = 'middle';
|
||||
|
||||
|
||||
context.fillText(render.player.info().name, nameCenterX, nameCenterY - render.fontSize / 2);
|
||||
context.fillText(String(Math.floor(render.player.troops())), nameCenterX, nameCenterY + render.fontSize);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
<body>
|
||||
<h1>Warfront</h1>
|
||||
<button id="startButton">Start Game</button>
|
||||
<div id="game-setup">
|
||||
<div id="lobbies-container">
|
||||
<h2>Available Lobbies</h2>
|
||||
|
||||
+2
-1
@@ -56,6 +56,7 @@ const IntentSchema = z.union([AttackIntentSchema, SpawnIntentSchema, BoatAttackI
|
||||
|
||||
const TurnSchema = z.object({
|
||||
turnNumber: z.number(),
|
||||
gameID: z.string(),
|
||||
intents: z.array(IntentSchema)
|
||||
})
|
||||
|
||||
@@ -87,7 +88,7 @@ const ClientBaseMessageSchema = z.object({
|
||||
export const ClientIntentMessageSchema = ClientBaseMessageSchema.extend({
|
||||
type: z.literal('intent'),
|
||||
clientID: z.string(),
|
||||
//gameID: z.string(),
|
||||
gameID: z.string(),
|
||||
intent: IntentSchema
|
||||
})
|
||||
|
||||
|
||||
@@ -28,10 +28,10 @@ export const defaultSettings = new class implements Settings {
|
||||
return 100
|
||||
}
|
||||
lobbyCreationRate(): number {
|
||||
return 5 * 1000
|
||||
return 2 * 1000
|
||||
}
|
||||
lobbyLifetime(): number {
|
||||
return 2 * 1000
|
||||
return 3 * 1000
|
||||
}
|
||||
theme(): Theme {return pastelTheme;}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ export class AttackExecution implements Execution {
|
||||
// }
|
||||
|
||||
|
||||
let numTilesPerTick = this._owner.borderTiles().size / 2
|
||||
let numTilesPerTick = this._owner.borderTiles().size / 5
|
||||
while (numTilesPerTick > 0) {
|
||||
if (this.troops < 1) {
|
||||
this.active = false
|
||||
|
||||
@@ -12,7 +12,7 @@ export class PlayerExecution implements Execution {
|
||||
}
|
||||
|
||||
tick(ticks: number) {
|
||||
this.player.addTroops(Math.sqrt(this.player.numTilesOwned() * this.player.troops() + 1000) / 1000 + 100)
|
||||
this.player.addTroops(Math.sqrt(this.player.numTilesOwned() * this.player.troops() + 1000) / 1000)
|
||||
}
|
||||
|
||||
owner(): MutablePlayer {
|
||||
|
||||
@@ -49,11 +49,11 @@ export class GameManager {
|
||||
tick() {
|
||||
const now = Date.now()
|
||||
|
||||
const active = this.lobbies().filter(l => !l.isExpired(now - 1000))
|
||||
const expired = this.lobbies().filter(l => l.isExpired(now - 1000))
|
||||
const active = this.lobbies().filter(l => !l.isExpired(now - 2000))
|
||||
const expired = this.lobbies().filter(l => l.isExpired(now - 2000))
|
||||
this._lobbies = new Map(active.map(lobby => [lobby.id, lobby]));
|
||||
expired.forEach(lobby => {
|
||||
const game = new GameServer(generateUniqueID(), lobby.clients, this.settings)
|
||||
const game = new GameServer(lobby.id, lobby.clients, this.settings)
|
||||
this.games.set(game.id, game)
|
||||
game.start()
|
||||
})
|
||||
|
||||
@@ -22,7 +22,11 @@ export class GameServer {
|
||||
c.ws.on('message', (message: string) => {
|
||||
const clientMsg: ClientMessage = ClientMessageSchema.parse(JSON.parse(message))
|
||||
if (clientMsg.type == "intent") {
|
||||
this.addIntent(clientMsg.intent)
|
||||
if (clientMsg.gameID == this.id) {
|
||||
this.addIntent(clientMsg.intent)
|
||||
} else {
|
||||
console.warn(`client ${clientMsg.clientID} sent to wrong game`)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -46,6 +50,7 @@ export class GameServer {
|
||||
private endTurn() {
|
||||
const pastTurn: Turn = {
|
||||
turnNumber: this.turns.length,
|
||||
gameID: this.id,
|
||||
intents: this.intents
|
||||
}
|
||||
this.turns.push(pastTurn)
|
||||
|
||||
@@ -27,7 +27,7 @@ const gm = new GameManager(defaultSettings)
|
||||
|
||||
// New GET endpoint to list lobbies
|
||||
app.get('/lobbies', (req, res) => {
|
||||
const lobbyList = Array.from(gm.lobbies()).map(lobby => ({
|
||||
const lobbyList = Array.from(gm.lobbies()).filter(l => !l.isExpired(Date.now())).map(lobby => ({
|
||||
id: lobby.id,
|
||||
}));
|
||||
|
||||
@@ -42,8 +42,12 @@ wss.on('connection', (ws) => {
|
||||
console.log(`got message ${message}`)
|
||||
const clientMsg: ClientMessage = ClientMessageSchema.parse(JSON.parse(message))
|
||||
if (clientMsg.type == "join") {
|
||||
console.log('got join request')
|
||||
if (gm.hasLobby(clientMsg.lobbyID)) {
|
||||
console.log('client joining lobby')
|
||||
gm.addClientToLobby(new Client(clientMsg.clientID, ws), clientMsg.lobbyID)
|
||||
} else {
|
||||
console.log('lobby not found')
|
||||
}
|
||||
}
|
||||
// TODO: send error message
|
||||
|
||||
Reference in New Issue
Block a user