mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-08-18 09:14:09 +00:00
Can select map on private lobbies
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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 => {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user