Can select map on private lobbies

This commit is contained in:
evanpelle
2024-10-16 20:34:57 -07:00
parent d92dc9eba6
commit 206f6d3333
6 changed files with 55 additions and 14 deletions
+6 -1
View File
@@ -46,7 +46,7 @@ export function joinLobby(lobbyConfig: LobbyConfig, onjoin: () => void): () => v
console.log('lobby: game started') console.log('lobby: game started')
onjoin() onjoin()
const gameConfig = { const gameConfig = {
map: GameMap.World, map: message.config?.gameMap || lobbyConfig.map,
clientID: clientID, clientID: clientID,
gameID: lobbyConfig.gameID, gameID: lobbyConfig.gameID,
ip: lobbyConfig.ip, ip: lobbyConfig.ip,
@@ -93,6 +93,7 @@ export class GameRunner {
private intervalID: NodeJS.Timeout private intervalID: NodeJS.Timeout
private isProcessingTurn = false private isProcessingTurn = false
private hasJoined = false
constructor( constructor(
private id: ClientID, private id: ClientID,
@@ -125,6 +126,7 @@ export class GameRunner {
}; };
const onmessage = (message: ServerMessage) => { const onmessage = (message: ServerMessage) => {
if (message.type == "start") { if (message.type == "start") {
this.hasJoined = true
console.log("starting game!") console.log("starting game!")
for (const turn of message.turns) { for (const turn of message.turns) {
if (turn.turnNumber < this.turns.length) { if (turn.turnNumber < this.turns.length) {
@@ -134,6 +136,9 @@ export class GameRunner {
} }
} }
if (message.type == "turn") { if (message.type == "turn") {
if (!this.hasJoined) {
return
}
if (this.turns.length != message.turn.turnNumber) { if (this.turns.length != message.turn.turnNumber) {
console.error(`got wrong turn have turns ${this.turns.length}, received turn ${message.turn.turnNumber}`) console.error(`got wrong turn have turns ${this.turns.length}, received turn ${message.turn.turnNumber}`)
} else { } else {
+13 -2
View File
@@ -111,7 +111,7 @@ export class HostLobbyModal extends LitElement {
<div> <div>
<label for="map-select">Map: </label> <label for="map-select">Map: </label>
<select id="map-select" @change=${this.handleMapChange}> <select id="map-select" @change=${this.handleMapChange}>
${Object.entries(new Set()) ${Object.entries(GameMap)
.filter(([key]) => isNaN(Number(key))) .filter(([key]) => isNaN(Number(key)))
.map(([key, value]) => html` .map(([key, value]) => html`
<option value=${value} ?selected=${this.selectedMap === value}> <option value=${value} ?selected=${this.selectedMap === value}>
@@ -152,8 +152,17 @@ export class HostLobbyModal extends LitElement {
this.copySuccess = false; this.copySuccess = false;
} }
private handleMapChange(e: Event) { private async handleMapChange(e: Event) {
this.selectedMap = Number((e.target as HTMLSelectElement).value) as GameMap; this.selectedMap = Number((e.target as HTMLSelectElement).value) as GameMap;
console.log(`updating map to ${this.selectedMap}`)
const response = await fetch(`/private_lobby/${this.lobbyId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({gameMap: this.selectedMap})
});
} }
private async startGame() { private async startGame() {
console.log(`Starting private game with map: ${GameMap[this.selectedMap]}`); console.log(`Starting private game with map: ${GameMap[this.selectedMap]}`);
@@ -166,6 +175,7 @@ export class HostLobbyModal extends LitElement {
}); });
} }
private async copyToClipboard() { private async copyToClipboard() {
try { try {
await navigator.clipboard.writeText(this.lobbyId); await navigator.clipboard.writeText(this.lobbyId);
@@ -180,6 +190,7 @@ export class HostLobbyModal extends LitElement {
} }
async function createLobby(): Promise<Lobby> { async function createLobby(): Promise<Lobby> {
try { try {
const response = await fetch('/private_lobby', { const response = await fetch('/private_lobby', {
+8 -2
View File
@@ -1,5 +1,5 @@
import {z} from 'zod'; import {z} from 'zod';
import {PlayerType} from './game/Game'; import {GameMap, PlayerType} from './game/Game';
export type GameID = string export type GameID = string
export type ClientID = string export type ClientID = string
@@ -27,6 +27,7 @@ export type EmojiIntent = z.infer<typeof EmojiIntentSchema>
export type DonateIntent = z.infer<typeof DonateIntentSchema> export type DonateIntent = z.infer<typeof DonateIntentSchema>
export type Turn = z.infer<typeof TurnSchema> export type Turn = z.infer<typeof TurnSchema>
export type GameConfig = z.infer<typeof GameConfigSchema>
export type ClientMessage = ClientIntentMessage | ClientJoinMessage | ClientLeaveMessage export type ClientMessage = ClientIntentMessage | ClientJoinMessage | ClientLeaveMessage
export type ServerMessage = ServerSyncMessage | ServerStartGameMessage export type ServerMessage = ServerSyncMessage | ServerStartGameMessage
@@ -49,6 +50,10 @@ export interface Lobby {
numClients?: number; numClients?: number;
} }
const GameConfigSchema = z.object({
gameMap: z.nativeEnum(GameMap)
})
const EmojiSchema = z.string().refine( const EmojiSchema = z.string().refine(
(val) => { (val) => {
return /\p{Emoji}/u.test(val); return /\p{Emoji}/u.test(val);
@@ -170,7 +175,8 @@ export const ServerTurnMessageSchema = ServerBaseMessageSchema.extend({
export const ServerStartGameMessageSchema = ServerBaseMessageSchema.extend({ export const ServerStartGameMessageSchema = ServerBaseMessageSchema.extend({
type: z.literal('start'), type: z.literal('start'),
// Turns the client missed if they are late to the game. // Turns the client missed if they are late to the game.
turns: z.array(TurnSchema) turns: z.array(TurnSchema),
config: GameConfigSchema
}) })
+13 -3
View File
@@ -1,8 +1,9 @@
import {Config} from "../core/configuration/Config"; import {Config} from "../core/configuration/Config";
import {ClientID, GameID} from "../core/Schemas"; import {ClientID, GameConfig, GameID} from "../core/Schemas";
import {v4 as uuidv4} from 'uuid'; import {v4 as uuidv4} from 'uuid';
import {Client} from "./Client"; import {Client} from "./Client";
import {GamePhase, GameServer} from "./GameServer"; import {GamePhase, GameServer} from "./GameServer";
import {GameMap} from "../core/game/Game";
@@ -27,9 +28,18 @@ export class GameManager {
game.addClient(client, lastTurn) game.addClient(client, lastTurn)
} }
updateGameConfig(gameID: GameID, gameConfig: GameConfig) {
const game = this.games.find(g => g.id == gameID)
if (game == null) {
console.warn(`game ${gameID} not found`)
return
}
game.updateGameConfig(gameConfig)
}
createPrivateGame(): string { createPrivateGame(): string {
const id = genSmallGameID() const id = genSmallGameID()
this.games.push(new GameServer(id, Date.now(), false, this.config)) this.games.push(new GameServer(id, Date.now(), false, this.config, {gameMap: GameMap.World}))
return id return id
} }
@@ -58,7 +68,7 @@ export class GameManager {
if (now > this.lastNewLobby + this.config.gameCreationRate()) { if (now > this.lastNewLobby + this.config.gameCreationRate()) {
this.lastNewLobby = now this.lastNewLobby = now
const id = uuidv4() const id = uuidv4()
lobbies.push(new GameServer(id, now, true, this.config)) lobbies.push(new GameServer(id, now, true, this.config, {gameMap: GameMap.World}))
} }
active.filter(g => !g.hasStarted() && g.isPublic).forEach(g => { active.filter(g => !g.hasStarted() && g.isPublic).forEach(g => {
+12 -3
View File
@@ -1,4 +1,4 @@
import {ClientMessage, ClientMessageSchema, Intent, ServerStartGameMessage, ServerStartGameMessageSchema, ServerTurnMessageSchema, Turn} from "../core/Schemas"; import {ClientMessage, ClientMessageSchema, GameConfig, Intent, ServerStartGameMessage, ServerStartGameMessageSchema, ServerTurnMessageSchema, Turn} from "../core/Schemas";
import {Config} from "../core/configuration/Config"; import {Config} from "../core/configuration/Config";
import {Client} from "./Client"; import {Client} from "./Client";
import WebSocket from 'ws'; import WebSocket from 'ws';
@@ -23,13 +23,21 @@ export class GameServer {
private endTurnIntervalID private endTurnIntervalID
constructor( constructor(
public readonly id: string, public readonly id: string,
public readonly createdAt: number, public readonly createdAt: number,
public readonly isPublic: boolean, public readonly isPublic: boolean,
private config: Config, private config: Config,
private gameConfig: GameConfig,
) { } ) { }
public updateGameConfig(gameConfig: GameConfig): void {
if (gameConfig.gameMap != null) {
this.gameConfig.gameMap = gameConfig.gameMap
}
}
public addClient(client: Client, lastTurn: number) { public addClient(client: Client, lastTurn: number) {
console.log(`game ${this.id} adding client ${client.id}`) console.log(`game ${this.id} adding client ${client.id}`)
slog('client_joined_game', `client ${client.id} (re)joining game ${this.id}`, { slog('client_joined_game', `client ${client.id} (re)joining game ${this.id}`, {
@@ -93,7 +101,8 @@ export class GameServer {
ws.send(JSON.stringify(ServerStartGameMessageSchema.parse( ws.send(JSON.stringify(ServerStartGameMessageSchema.parse(
{ {
type: "start", type: "start",
turns: this.turns.slice(lastTurn) turns: this.turns.slice(lastTurn),
config: this.gameConfig
} }
))) )))
} }
@@ -137,7 +146,7 @@ export class GameServer {
if (!this.isPublic) { if (!this.isPublic) {
if (this._hasStarted) { if (this._hasStarted) {
if (this.clients.length == 0) { if (this.clients.length == 0) {
console.log(`game ${this.id} is finisehd`) console.log()
return GamePhase.Finished return GamePhase.Finished
} else { } else {
return GamePhase.Active return GamePhase.Active
+3 -3
View File
@@ -23,8 +23,6 @@ app.use(express.static(path.join(__dirname, '../../out')));
app.use(express.json()) app.use(express.json())
const gm = new GameManager(getConfig()) const gm = new GameManager(getConfig())
const privateGames = new Map<string, GameServer>()
// New GET endpoint to list lobbies // New GET endpoint to list lobbies
app.get('/lobbies', (req, res) => { app.get('/lobbies', (req, res) => {
const now = Date.now() const now = Date.now()
@@ -35,6 +33,7 @@ app.get('/lobbies', (req, res) => {
}); });
}); });
app.post('/private_lobby', (req, res) => { app.post('/private_lobby', (req, res) => {
const id = gm.createPrivateGame() const id = gm.createPrivateGame()
console.log('creating private lobby with id ${id}') console.log('creating private lobby with id ${id}')
@@ -48,8 +47,9 @@ app.post('/start_private_lobby/:id', (req, res) => {
gm.startPrivateGame(req.params.id) gm.startPrivateGame(req.params.id)
}); });
app.put('/private_lobby/:id', (req, res) => { app.put('/private_lobby/:id', (req, res) => {
const lobbyID = req.params.id
gm.updateGameConfig(lobbyID, {gameMap: req.body.gameMap})
}); });
app.get('/lobby/:id/exists', (req, res) => { app.get('/lobby/:id/exists', (req, res) => {