diff --git a/src/client/GameRunner.ts b/src/client/GameRunner.ts index 8aed72d40..b1f118c1d 100644 --- a/src/client/GameRunner.ts +++ b/src/client/GameRunner.ts @@ -2,7 +2,6 @@ import { Executor } from "../core/execution/ExecutionManager"; import { Cell, MutableGame, PlayerEvent, PlayerID, MutablePlayer, TileEvent, Player, Game, UnitEvent, Tile, PlayerType, GameMap, Difficulty, GameType } from "../core/game/Game"; import { createGame } from "../core/game/GameImpl"; import { EventBus } from "../core/EventBus"; -import { Config, getConfig } from "../core/configuration/Config"; import { createRenderer, GameRenderer } from "./graphics/GameRenderer"; import { InputHandler, MouseUpEvent, ZoomEvent, DragEvent, MouseDownEvent } from "./InputHandler" import { ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, ClientMessageSchema, GameConfig, GameID, Intent, ServerMessage, ServerMessageSchema, ServerSyncMessage, Turn } from "../core/Schemas"; @@ -14,6 +13,7 @@ import { createCanvas } from "./Utils"; import { DisplayMessageEvent, MessageType } from "./graphics/layers/EventsDisplay"; import { WorkerClient } from "../core/worker/WorkerClient"; import { consolex, initRemoteSender } from "../core/Consolex"; +import { getConfig, getServerConfig } from "../core/configuration/Config"; export interface LobbyConfig { playerName: () => string @@ -32,7 +32,7 @@ export function joinLobby(lobbyConfig: LobbyConfig, onjoin: () => void): () => v consolex.log(`joinging lobby: gameID: ${lobbyConfig.gameID}, clientID: ${lobbyConfig.clientID}, persistentID: ${lobbyConfig.persistentID}`) - const config = getConfig() + const serverConfig = getServerConfig() let gameConfig: GameConfig = null if (lobbyConfig.gameType == GameType.Singleplayer) { @@ -47,7 +47,7 @@ export function joinLobby(lobbyConfig: LobbyConfig, onjoin: () => void): () => v lobbyConfig, gameConfig, eventBus, - config, + serverConfig, ) const onconnect = () => { @@ -70,11 +70,11 @@ export function joinLobby(lobbyConfig: LobbyConfig, onjoin: () => void): () => v export async function createClientGame(lobbyConfig: LobbyConfig, gameConfig: GameConfig, eventBus: EventBus, transport: Transport): Promise { - const config = getConfig() + const config = getConfig(gameConfig) const terrainMap = await loadTerrainMap(gameConfig.gameMap); - let game = createGame(terrainMap.map, terrainMap.miniMap, eventBus, config, gameConfig) + let game = createGame(terrainMap.map, terrainMap.miniMap, eventBus, config) const worker = new WorkerClient(game, gameConfig.gameMap) consolex.log('going to init path finder') diff --git a/src/client/LocalServer.ts b/src/client/LocalServer.ts index e8197692f..56ee39bfa 100644 --- a/src/client/LocalServer.ts +++ b/src/client/LocalServer.ts @@ -1,4 +1,4 @@ -import { Config } from "../core/configuration/Config"; +import { Config, ServerConfig } from "../core/configuration/Config"; import { consolex } from "../core/Consolex"; import { ClientID, ClientMessage, ClientMessageSchema, GameConfig, GameID, GameRecordSchema, Intent, PlayerRecord, ServerMessage, ServerStartGameMessageSchema, ServerTurnMessageSchema, Turn } from "../core/Schemas"; import { CreateGameRecord, generateID } from "../core/Util"; @@ -16,7 +16,7 @@ export class LocalServer { constructor( - private config: Config, + private serverConfig: ServerConfig, private gameConfig: GameConfig, private lobbyConfig: LobbyConfig, private clientConnect: () => void, @@ -26,7 +26,7 @@ export class LocalServer { start() { this.startedAt = Date.now() - this.endTurnIntervalID = setInterval(() => this.endTurn(), this.config.turnIntervalMs()); + this.endTurnIntervalID = setInterval(() => this.endTurn(), this.serverConfig.turnIntervalMs()); this.clientConnect() this.clientMessage(ServerStartGameMessageSchema.parse({ type: "start", diff --git a/src/client/PersistentID.ts b/src/client/PersistentID.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/client/Transport.ts b/src/client/Transport.ts index acfac9089..11c8eafcf 100644 --- a/src/client/Transport.ts +++ b/src/client/Transport.ts @@ -1,4 +1,4 @@ -import { Config } from "../core/configuration/Config" +import { Config, ServerConfig } from "../core/configuration/Config" import { SendLogEvent } from "../core/Consolex" import { EventBus, GameEvent } from "../core/EventBus" import { AllianceRequest, AllPlayers, Cell, GameType, Player, PlayerID, PlayerType, Tile, UnitType } from "../core/game/Game" @@ -104,7 +104,7 @@ export class Transport { // gameConfig only set on private games private gameConfig: GameConfig | null, private eventBus: EventBus, - private config: Config, + private serverConfig: ServerConfig, ) { this.isLocal = lobbyConfig.gameType == GameType.Singleplayer @@ -154,7 +154,7 @@ export class Transport { } private connectLocal(onconnect: () => void, onmessage: (message: ServerMessage) => void) { - this.localServer = new LocalServer(this.config, this.gameConfig, this.lobbyConfig, onconnect, onmessage) + this.localServer = new LocalServer(this.serverConfig, this.gameConfig, this.lobbyConfig, onconnect, onmessage) this.localServer.start() } diff --git a/src/client/graphics/layers/UILayer.ts b/src/client/graphics/layers/UILayer.ts index 8912a76de..4d3ecbfa6 100644 --- a/src/client/graphics/layers/UILayer.ts +++ b/src/client/graphics/layers/UILayer.ts @@ -35,7 +35,7 @@ export class UILayer implements Layer { const barHeight = 15; const barBackgroundWidth = this.transformHandler.width(); - const ratio = this.game.ticks() / this.game.config().numSpawnPhaseTurns(this.game.gameConfig().gameType) + const ratio = this.game.ticks() / this.game.config().numSpawnPhaseTurns(this.game.config().gameConfig().gameType) // Draw bar background context.fillStyle = 'rgba(0, 0, 0, 0.5)'; diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index 1b1fe8d46..f2991967d 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -1,21 +1,35 @@ import { Difficulty, GameType, Gold, Player, PlayerID, PlayerInfo, TerraNullius, Tick, Tile, Unit, UnitInfo, UnitType } from "../game/Game"; import { Colord, colord } from "colord"; -import { devConfig } from "./DevConfig"; -import { GameID } from "../Schemas"; import { preprodConfig } from "./PreprodConfig"; import { prodConfig } from "./ProdConfig"; import { consolex } from "../Consolex"; +import { GameConfig } from "../Schemas"; +import { DefaultConfig } from "./DefaultConfig"; +import { DevConfig, DevServerConfig } from "./DevConfig"; export enum GameEnv { Dev, Prod } +export function getConfig(gameConfig: GameConfig): Config { + const sc = getServerConfig() + switch (process.env.GAME_ENV) { + case 'dev': + return new DevConfig(sc, gameConfig) + case 'preprod': + case 'prod': + consolex.log('using prod config') + return new DefaultConfig(sc, gameConfig) + default: + throw Error(`unsupported server configuration: ${process.env.GAME_ENV}`) + } +} -export function getConfig(): Config { +export function getServerConfig(): ServerConfig { switch (process.env.GAME_ENV) { case 'dev': consolex.log('using dev config') - return devConfig + return new DevServerConfig() case 'preprod': consolex.log('using preprod config') return preprodConfig @@ -27,17 +41,17 @@ export function getConfig(): Config { } } -export function getGameEnv(): GameEnv { - return GameEnv.Prod -} - -export interface Config { - discordBotSecret(): string - theme(): Theme; - percentageTilesOwnedToWin(): number +export interface ServerConfig { turnIntervalMs(): number gameCreationRate(): number lobbyLifetime(): number +} + +export interface Config { + serverConfig(): ServerConfig + gameConfig(): GameConfig + theme(): Theme; + percentageTilesOwnedToWin(): number numBots(): number spawnNPCs(): boolean numSpawnPhaseTurns(gameType: GameType): number diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index fc2515c1c..3bd6cc976 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -1,13 +1,35 @@ import { Difficulty, GameType, Gold, Player, PlayerInfo, PlayerType, TerrainType, TerraNullius, Tick, Tile, Unit, UnitInfo, UnitType } from "../game/Game"; -import { GameID } from "../Schemas"; +import { GameConfig } from "../Schemas"; import { assertNever, distSort, manhattanDist, simpleHash, within } from "../Util"; -import { Config, Theme } from "./Config"; +import { Config, ServerConfig, Theme } from "./Config"; import { pastelTheme } from "./PastelTheme"; +export abstract class DefaultServerConfig implements ServerConfig { + turnIntervalMs(): number { + return 100 + } + gameCreationRate(): number { + return 10 * 60 * 1000 + } + lobbyLifetime(): number { + return 10 * 120 * 1000 + } +} -export abstract class DefaultConfig implements Config { - abstract discordBotSecret(): string +export class DefaultConfig implements Config { + + constructor(private _serverConfig: ServerConfig, private _gameConfig: GameConfig) { + + } + + gameConfig(): GameConfig { + return this._gameConfig + } + + serverConfig(): ServerConfig { + return this._serverConfig + } difficultyModifier(difficulty: Difficulty): number { switch (difficulty) { @@ -162,15 +184,6 @@ export abstract class DefaultConfig implements Config { numBots(): number { return 400 } - turnIntervalMs(): number { - return 100 - } - gameCreationRate(): number { - return 10 * 60 * 1000 - } - lobbyLifetime(): number { - return 10 * 120 * 1000 - } theme(): Theme { return pastelTheme; } attackLogic(attackTroops: number, attacker: Player, defender: Player | TerraNullius, tileToConquer: Tile): { attackerTroopLoss: number; defenderTroopLoss: number; tilesPerTickUsed: number } { diff --git a/src/core/configuration/DevConfig.ts b/src/core/configuration/DevConfig.ts index e7d88c99a..c063d2eca 100644 --- a/src/core/configuration/DevConfig.ts +++ b/src/core/configuration/DevConfig.ts @@ -1,20 +1,9 @@ import { GameType, Player, PlayerInfo, UnitInfo, UnitType } from "../game/Game"; -import { DefaultConfig } from "./DefaultConfig"; +import { GameConfig } from "../Schemas"; +import { ServerConfig } from "./Config"; +import { DefaultConfig, DefaultServerConfig } from "./DefaultConfig"; -export const devConfig = new class extends DefaultConfig { - discordBotSecret(): string { - throw new Error("Method not implemented."); - } - unitInfo(type: UnitType): UnitInfo { - const info = super.unitInfo(type) - const oldCost = info.cost - info.cost = (p: Player) => oldCost(p) / 10000 - return info - } - - percentageTilesOwnedToWin(): number { - return 95 - } +export class DevServerConfig extends DefaultServerConfig { numSpawnPhaseTurns(gameType: GameType): number { return gameType == GameType.Singleplayer ? 40 : 200 // return 100 @@ -25,6 +14,24 @@ export const devConfig = new class extends DefaultConfig { lobbyLifetime(): number { return 10 * 1000 } +} + +export class DevConfig extends DefaultConfig { + + constructor(sc: ServerConfig, gc: GameConfig) { + super(sc, gc); + } + + unitInfo(type: UnitType): UnitInfo { + const info = super.unitInfo(type) + const oldCost = info.cost + info.cost = (p: Player) => oldCost(p) / 10000 + return info + } + + percentageTilesOwnedToWin(): number { + return 95 + } // tradeShipSpawnRate(): number { // return 10 // } @@ -39,4 +46,4 @@ export const devConfig = new class extends DefaultConfig { // return false // } -} \ No newline at end of file +} diff --git a/src/core/configuration/PreprodConfig.ts b/src/core/configuration/PreprodConfig.ts index 196312ece..935bb152d 100644 --- a/src/core/configuration/PreprodConfig.ts +++ b/src/core/configuration/PreprodConfig.ts @@ -1,8 +1,5 @@ -import { DefaultConfig } from "./DefaultConfig"; +import { DefaultConfig, DefaultServerConfig } from "./DefaultConfig"; -export const preprodConfig = new class extends DefaultConfig { - discordBotSecret(): string { - throw new Error("Method not implemented."); - } +export const preprodConfig = new class extends DefaultServerConfig { } \ No newline at end of file diff --git a/src/core/configuration/ProdConfig.ts b/src/core/configuration/ProdConfig.ts index d636786be..116ba01e2 100644 --- a/src/core/configuration/ProdConfig.ts +++ b/src/core/configuration/ProdConfig.ts @@ -1,8 +1,5 @@ -import { DefaultConfig } from "./DefaultConfig"; +import { DefaultConfig, DefaultServerConfig } from "./DefaultConfig"; -export const prodConfig = new class extends DefaultConfig { - discordBotSecret(): string { - throw new Error("Method not implemented."); - } +export const prodConfig = new class extends DefaultServerConfig { } \ No newline at end of file diff --git a/src/core/execution/ExecutionManager.ts b/src/core/execution/ExecutionManager.ts index 8795cc603..20adac779 100644 --- a/src/core/execution/ExecutionManager.ts +++ b/src/core/execution/ExecutionManager.ts @@ -126,7 +126,7 @@ export class Executor { this.random.nextID() ), nation.cell, - nation.strength * this.gs.config().difficultyModifier(this.gs.gameConfig().difficulty) + nation.strength * this.gs.config().difficultyModifier(this.gs.config().gameConfig().difficulty) )) } return execs diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index a4698603d..684e4aed0 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -314,7 +314,6 @@ export interface Game { addExecution(...exec: Execution[]): void nations(): Nation[] config(): Config - gameConfig(): GameConfig displayMessage(message: string, type: MessageType, playerID: PlayerID | null): void units(...types: UnitType[]): Unit[] unitInfo(type: UnitType): UnitInfo diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 0668fad15..28c4c9f4d 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -13,8 +13,8 @@ import { DisplayMessageEvent, MessageType } from "../../client/graphics/layers/E import { UnitImpl } from "./UnitImpl"; import { consolex } from "../Consolex"; -export function createGame(terrainMap: TerrainMapImpl, miniMap: TerrainMap, eventBus: EventBus, config: Config, gameConfig: GameConfig): Game { - return new GameImpl(terrainMap, miniMap, eventBus, config, gameConfig) +export function createGame(terrainMap: TerrainMapImpl, miniMap: TerrainMap, eventBus: EventBus, config: Config): Game { + return new GameImpl(terrainMap, miniMap, eventBus, config) } export type CellString = string @@ -45,7 +45,6 @@ export class GameImpl implements MutableGame { private _miniMap: TerrainMap, public eventBus: EventBus, private _config: Config, - private _gameConfig: GameConfig, ) { this._terraNullius = new TerraNulliusImpl(this) this._width = _terrainMap.width(); @@ -67,10 +66,6 @@ export class GameImpl implements MutableGame { )) } - gameConfig(): GameConfig { - return this._gameConfig - } - addFallout(tile: Tile) { const ti = tile as TileImpl if (tile.hasOwner()) { @@ -149,7 +144,7 @@ export class GameImpl implements MutableGame { } inSpawnPhase(): boolean { - return this._ticks <= this.config().numSpawnPhaseTurns(this._gameConfig.gameType) + return this._ticks <= this.config().numSpawnPhaseTurns(this.config().gameConfig().gameType) } ticks(): number { diff --git a/src/server/GameManager.ts b/src/server/GameManager.ts index d2ca717f4..615a577e0 100644 --- a/src/server/GameManager.ts +++ b/src/server/GameManager.ts @@ -1,4 +1,4 @@ -import { Config } from "../core/configuration/Config"; +import { Config, ServerConfig } from "../core/configuration/Config"; import { ClientID, GameConfig, GameID } from "../core/Schemas"; import { v4 as uuidv4 } from 'uuid'; import { Client } from "./Client"; @@ -14,7 +14,7 @@ export class GameManager { private games: GameServer[] = [] - constructor(private config: Config) { } + constructor(private config: ServerConfig) { } public game(id: GameID): GameServer | null { return this.games.find(g => g.id == id) diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index ff9b7511d..776e775bd 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -1,5 +1,5 @@ import { ClientID, ClientMessage, ClientMessageSchema, GameConfig, GameRecordSchema, Intent, PlayerRecord, ServerPingMessageSchema, ServerStartGameMessage, ServerStartGameMessageSchema, ServerTurnMessageSchema, Turn } from "../core/Schemas"; -import { Config } from "../core/configuration/Config"; +import { Config, ServerConfig } from "../core/configuration/Config"; import { Client } from "./Client"; import WebSocket from 'ws'; import { slog } from "./StructuredLog"; @@ -35,7 +35,7 @@ export class GameServer { public readonly id: string, public readonly createdAt: number, public readonly isPublic: boolean, - private config: Config, + private config: ServerConfig, private gameConfig: GameConfig, ) { } diff --git a/src/server/Server.ts b/src/server/Server.ts index efc2af5c7..31994516d 100644 --- a/src/server/Server.ts +++ b/src/server/Server.ts @@ -5,7 +5,7 @@ import path from 'path'; import { fileURLToPath } from 'url'; import { GameManager } from './GameManager'; import { ClientMessage, ClientMessageSchema, GameRecord, GameRecordSchema, LogSeverity } from '../core/Schemas'; -import { getConfig } from '../core/configuration/Config'; +import { getConfig, getServerConfig } from '../core/configuration/Config'; import { slog } from './StructuredLog'; import { Client } from './Client'; import { GamePhase, GameServer } from './GameServer'; @@ -23,7 +23,7 @@ const wss = new WebSocketServer({ server }); app.use(express.static(path.join(__dirname, '../../out'))); app.use(express.json()) -const gm = new GameManager(getConfig()) +const gm = new GameManager(getServerConfig()) const bot = new DiscordBot(); try {