From 684b0bb2e0309cbbb1c7f08054017e1bad04033d Mon Sep 17 00:00:00 2001 From: NewHappyRabbit <31893343+NewHappyRabbit@users.noreply.github.com> Date: Tue, 11 Feb 2025 03:29:56 +0200 Subject: [PATCH 1/2] Added checkboxes to disable Bots and NPCs for single player and private lobbies --- .gitignore | 3 ++- src/client/ClientGameRunner.ts | 4 +++ src/client/HostLobbyModal.ts | 34 +++++++++++++++++++++++++ src/client/Main.ts | 2 ++ src/client/SinglePlayerModal.ts | 26 +++++++++++++++++++ src/core/GameRunner.ts | 8 +++--- src/core/Schemas.ts | 2 ++ src/core/configuration/Config.ts | 1 + src/core/configuration/DefaultConfig.ts | 5 +++- src/server/GameManager.ts | 4 +++ src/server/GameServer.ts | 6 +++++ src/server/Server.ts | 2 ++ 12 files changed, 92 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 976703fa9..8652c3158 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ TODO.txt resources/images/.DS_Store resources/.DS_Store .env -.prettierrc \ No newline at end of file +.prettierrc +.prettierignore \ No newline at end of file diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index 298fbaa5e..7f3ec7904 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -56,6 +56,8 @@ export interface LobbyConfig { gameID: GameID; map: GameMapType | null; difficulty: Difficulty | null; + disableBots: boolean | null; + disableNPCs: boolean | null; } export function joinLobby( @@ -77,6 +79,8 @@ export function joinLobby( gameType: GameType.Singleplayer, gameMap: lobbyConfig.map, difficulty: lobbyConfig.difficulty, + disableBots: lobbyConfig.disableBots, + disableNPCs: lobbyConfig.disableNPCs, }; } diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts index 8233cd584..28dc377d7 100644 --- a/src/client/HostLobbyModal.ts +++ b/src/client/HostLobbyModal.ts @@ -9,6 +9,8 @@ export class HostLobbyModal extends LitElement { @state() private isModalOpen = false; @state() private selectedMap: GameMapType = GameMapType.World; @state() private selectedDiffculty: Difficulty = Difficulty.Medium; + @state() private disableNPCs = false; + @state() private disableBots = false; @state() private lobbyId = ""; @state() private copySuccess = false; @state() private players: string[] = []; @@ -165,6 +167,22 @@ export class HostLobbyModal extends LitElement { )} +
+ + +
+
+ + +

Players: ${this.players.join(", ")}

@@ -191,6 +209,8 @@ export class HostLobbyModal extends LitElement { }, map: this.selectedMap, difficulty: this.selectedDiffculty, + disableBots: this.disableBots, + disableNPCs: this.disableNPCs, }, bubbles: true, composed: true, @@ -226,6 +246,18 @@ export class HostLobbyModal extends LitElement { this.putGameConfig(); } + private async handleDisableBotsChange(e: Event) { + this.disableBots = Boolean((e.target as HTMLInputElement).checked); + consolex.log(`updating disable bots to ${this.disableBots}`); + this.putGameConfig(); + } + + private async handleDisableNPCsChange(e: Event) { + this.disableNPCs = Boolean((e.target as HTMLInputElement).checked); + consolex.log(`updating disable npcs to ${this.disableNPCs}`); + this.putGameConfig(); + } + private async putGameConfig() { const response = await fetch(`/private_lobby/${this.lobbyId}`, { method: "PUT", @@ -235,6 +267,8 @@ export class HostLobbyModal extends LitElement { body: JSON.stringify({ gameMap: this.selectedMap, difficulty: this.selectedDiffculty, + disableBots: this.disableBots, + disableNPCs: this.disableNPCs, }), }); } diff --git a/src/client/Main.ts b/src/client/Main.ts index dede86905..52b586cff 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -95,6 +95,8 @@ class Client { clientID: generateID(), map: event.detail.map, difficulty: event.detail.difficulty, + disableBots: event.detail.disableBots, + disableNPCs: event.detail.disableNPCs, }, () => this.joinModal.close() ); diff --git a/src/client/SinglePlayerModal.ts b/src/client/SinglePlayerModal.ts index cc3f98480..9645b25f3 100644 --- a/src/client/SinglePlayerModal.ts +++ b/src/client/SinglePlayerModal.ts @@ -9,6 +9,8 @@ export class SinglePlayerModal extends LitElement { @state() private isModalOpen = false; @state() private selectedMap: GameMapType = GameMapType.World; @state() private selectedDifficulty: Difficulty = Difficulty.Medium; + @state() private disableNPCs = false; + @state() private disableBots = false; static styles = css` .modal-overlay { @@ -115,6 +117,22 @@ export class SinglePlayerModal extends LitElement { )}
+
+ + +
+
+ + +
@@ -140,6 +158,12 @@ export class SinglePlayerModal extends LitElement { (e.target as HTMLSelectElement).value, ) as Difficulty; } + private handleDisableBotsChange(e: Event) { + this.disableBots = Boolean((e.target as HTMLInputElement).checked); + } + private handleDisableNPCsChange(e: Event) { + this.disableNPCs = Boolean((e.target as HTMLInputElement).checked); + } private startGame() { consolex.log( `Starting single player game with map: ${GameMapType[this.selectedMap]}`, @@ -153,6 +177,8 @@ export class SinglePlayerModal extends LitElement { }, map: this.selectedMap, difficulty: this.selectedDifficulty, + disableBots: this.disableBots, + disableNPCs: this.disableNPCs, }, bubbles: true, composed: true, diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index b9d07fcca..38bbaa244 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -58,9 +58,11 @@ export class GameRunner { ) {} init() { - this.game.addExecution( - ...this.execManager.spawnBots(this.game.config().numBots()) - ); + if (this.game.config().spawnBots()) { + this.game.addExecution( + ...this.execManager.spawnBots(this.game.config().numBots()) + ); + } if (this.game.config().spawnNPCs()) { this.game.addExecution(...this.execManager.fakeHumanExecutions()); } diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index 1c402ccf9..033512851 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -92,6 +92,8 @@ const GameConfigSchema = z.object({ gameMap: z.nativeEnum(GameMapType), difficulty: z.nativeEnum(Difficulty), gameType: z.nativeEnum(GameType), + disableBots: z.boolean(), + disableNPCs: z.boolean(), }); const SafeString = z diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index 1b527c327..09cd5644a 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -70,6 +70,7 @@ export interface Config { percentageTilesOwnedToWin(): number; numBots(): number; spawnNPCs(): boolean; + spawnBots(): boolean; numSpawnPhaseTurns(): number; startManpower(playerInfo: PlayerInfo): number; diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index f210a4450..7f5881410 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -76,7 +76,10 @@ export class DefaultConfig implements Config { return 5; } spawnNPCs(): boolean { - return true; + return !this._gameConfig.disableNPCs; + } + spawnBots(): boolean { + return !this._gameConfig.disableBots; } tradeShipGold(dist: number): Gold { return 10000 + 100 * Math.pow(dist, 1.1); diff --git a/src/server/GameManager.ts b/src/server/GameManager.ts index b4ff3f7c0..6a98b6f41 100644 --- a/src/server/GameManager.ts +++ b/src/server/GameManager.ts @@ -49,6 +49,8 @@ export class GameManager { gameMap: GameMapType.World, gameType: GameType.Private, difficulty: Difficulty.Medium, + disableBots: false, + disableNPCs: false, }) ); return id; @@ -87,6 +89,8 @@ export class GameManager { gameMap: this.random.randElement(Object.values(GameMapType)), gameType: GameType.Public, difficulty: Difficulty.Medium, + disableBots: false, + disableNPCs: false, }) ); } diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index df12a0013..a69acaaa6 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -57,6 +57,12 @@ export class GameServer { if (gameConfig.difficulty != null) { this.gameConfig.difficulty = gameConfig.difficulty; } + if (gameConfig.disableBots != null) { + this.gameConfig.disableBots = gameConfig.disableBots; + } + if (gameConfig.disableNPCs != null) { + this.gameConfig.disableNPCs = gameConfig.disableNPCs; + } } public addClient(client: Client, lastTurn: number) { diff --git a/src/server/Server.ts b/src/server/Server.ts index baa8030fc..90d847ef9 100644 --- a/src/server/Server.ts +++ b/src/server/Server.ts @@ -94,6 +94,8 @@ app.put("/private_lobby/:id", (req, res) => { gm.updateGameConfig(lobbyID, { gameMap: req.body.gameMap, difficulty: req.body.difficulty, + disableBots: req.body.disableBots, + disableNPCs: req.body.disableNPCs, }); }); From fbd6c50b1155401b8a4b3366f5fb05d7624b3959 Mon Sep 17 00:00:00 2001 From: NewHappyRabbit Date: Tue, 11 Feb 2025 15:07:29 +0200 Subject: [PATCH 2/2] Added cross-env for cross-platform env variables in node. --- package-lock.json | 20 +++++ package.json | 201 +++++++++++++++++++++++----------------------- 2 files changed, 121 insertions(+), 100 deletions(-) diff --git a/package-lock.json b/package-lock.json index c5cd8241c..0c3c4aced 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,6 +65,7 @@ "binary-base64-loader": "^1.0.0", "chai": "^5.1.1", "concurrently": "^8.2.2", + "cross-env": "^7.0.3", "css-loader": "^7.1.2", "file-loader": "^6.2.0", "html-inline-script-webpack-plugin": "^3.2.1", @@ -6612,6 +6613,25 @@ "dev": true, "license": "MIT" }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", diff --git a/package.json b/package.json index 22faecb11..33ee294ee 100644 --- a/package.json +++ b/package.json @@ -1,102 +1,103 @@ { - "name": "openfront-client", - "scripts": { - "build-map": "node --loader ts-node/esm --experimental-specifier-resolution=node src/scripts/TerrainMapGenerator.ts", - "build-dev": "webpack --config webpack.config.js --mode development", - "build-prod": "webpack --config webpack.config.js --mode production", - "start:client": "webpack serve --open --node-env development", - "start:server": "node --loader ts-node/esm --experimental-specifier-resolution=node src/server/Server.ts", - "start:server-dev": "GAME_ENV=dev node --loader ts-node/esm --experimental-specifier-resolution=node src/server/Server.ts", - "dev": "GAME_ENV=dev concurrently \"npm run start:client\" \"npm run start:server-dev\"", - "tunnel": "npm run build-prod && npm run start:server", - "test": "jest" - }, - "devDependencies": { - "@babel/core": "^7.25.2", - "@babel/preset-env": "^7.25.3", - "@babel/preset-typescript": "^7.24.7", - "@types/chai": "^4.3.17", - "@types/d3": "^7.4.3", - "@types/jest": "^29.5.12", - "@types/jquery": "^3.5.31", - "@types/mocha": "^10.0.7", - "@types/node": "^22.10.2", - "@types/sinon": "^17.0.3", - "@types/uuid": "^10.0.0", - "@types/ws": "^8.5.11", - "autoprefixer": "^10.4.20", - "babel-jest": "^29.7.0", - "binary-base64-loader": "^1.0.0", - "chai": "^5.1.1", - "concurrently": "^8.2.2", - "css-loader": "^7.1.2", - "file-loader": "^6.2.0", - "html-inline-script-webpack-plugin": "^3.2.1", - "html-loader": "^5.1.0", - "html-webpack-plugin": "^5.6.0", - "jest": "^29.7.0", - "mocha": "^10.7.0", - "mrmime": "^2.0.0", - "postcss": "^8.5.1", - "postcss-loader": "^8.1.1", - "raw-loader": "^4.0.2", - "sinon": "^18.0.0", - "sinon-chai": "^4.0.0", - "style-loader": "^4.0.0", - "tailwindcss": "^3.4.17", - "ts-jest": "^29.2.4", - "ts-loader": "^9.5.1", - "ts-mocha": "^10.0.0", - "ts-node": "^10.9.2", - "tsconfig-paths": "^4.2.0", - "tsx": "^4.17.0", - "typescript": "^5.7.2", - "webpack": "^5.91.0", - "webpack-cli": "^5.1.4", - "webpack-dev-server": "^5.0.4", - "worker-loader": "^3.0.8" - }, - "dependencies": { - "@datastructures-js/priority-queue": "^6.3.1", - "@google-cloud/bigquery": "^7.9.1", - "@google-cloud/secret-manager": "^5.6.0", - "@google-cloud/storage": "^7.14.0", - "@types/dompurify": "^3.0.5", - "@types/express": "^4.17.21", - "@types/google-protobuf": "^3.15.12", - "@types/hammerjs": "^2.0.45", - "@types/jimp": "^0.2.28", - "@types/msgpack5": "^3.4.6", - "@types/raphael": "^2.3.9", - "@types/twemoji": "^13.1.1", - "binary-loader": "^0.0.1", - "colord": "^2.9.3", - "crypto": "^1.0.1", - "d3": "^7.9.0", - "discord.js": "^14.16.3", - "dompurify": "^3.1.7", - "dotenv": "^16.4.7", - "express": "^4.21.1", - "google-auth-library": "^9.14.0", - "googleapis": "^143.0.0", - "hammerjs": "^2.0.8", - "ip-anonymize": "^0.1.0", - "jimp": "^0.22.12", - "lit": "^3.2.1", - "msgpack5": "^6.0.2", - "nanoid": "^5.0.9", - "node-addon-api": "^8.1.0", - "node-gyp": "^10.2.0", - "obscenity": "^0.4.3", - "priority-queue-typescript": "^1.0.1", - "protobufjs": "^7.3.2", - "pureimage": "^0.4.13", - "raphael": "^2.3.0", - "twemoji": "^14.0.2", - "uuid": "^10.0.0", - "wheelnav": "^1.7.1", - "ws": "^8.18.0", - "zod": "^3.23.8" - }, - "type": "module" + "name": "openfront-client", + "scripts": { + "build-map": "node --loader ts-node/esm --experimental-specifier-resolution=node src/scripts/TerrainMapGenerator.ts", + "build-dev": "webpack --config webpack.config.js --mode development", + "build-prod": "webpack --config webpack.config.js --mode production", + "start:client": "webpack serve --open --node-env development", + "start:server": "node --loader ts-node/esm --experimental-specifier-resolution=node src/server/Server.ts", + "start:server-dev": "cross-env GAME_ENV=dev node --loader ts-node/esm --experimental-specifier-resolution=node src/server/Server.ts", + "dev": "cross-env GAME_ENV=dev concurrently \"npm run start:client\" \"npm run start:server-dev\"", + "tunnel": "npm run build-prod && npm run start:server", + "test": "jest" + }, + "devDependencies": { + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.3", + "@babel/preset-typescript": "^7.24.7", + "@types/chai": "^4.3.17", + "@types/d3": "^7.4.3", + "@types/jest": "^29.5.12", + "@types/jquery": "^3.5.31", + "@types/mocha": "^10.0.7", + "@types/node": "^22.10.2", + "@types/sinon": "^17.0.3", + "@types/uuid": "^10.0.0", + "@types/ws": "^8.5.11", + "autoprefixer": "^10.4.20", + "babel-jest": "^29.7.0", + "binary-base64-loader": "^1.0.0", + "chai": "^5.1.1", + "concurrently": "^8.2.2", + "cross-env": "^7.0.3", + "css-loader": "^7.1.2", + "file-loader": "^6.2.0", + "html-inline-script-webpack-plugin": "^3.2.1", + "html-loader": "^5.1.0", + "html-webpack-plugin": "^5.6.0", + "jest": "^29.7.0", + "mocha": "^10.7.0", + "mrmime": "^2.0.0", + "postcss": "^8.5.1", + "postcss-loader": "^8.1.1", + "raw-loader": "^4.0.2", + "sinon": "^18.0.0", + "sinon-chai": "^4.0.0", + "style-loader": "^4.0.0", + "tailwindcss": "^3.4.17", + "ts-jest": "^29.2.4", + "ts-loader": "^9.5.1", + "ts-mocha": "^10.0.0", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "tsx": "^4.17.0", + "typescript": "^5.7.2", + "webpack": "^5.91.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.0.4", + "worker-loader": "^3.0.8" + }, + "dependencies": { + "@datastructures-js/priority-queue": "^6.3.1", + "@google-cloud/bigquery": "^7.9.1", + "@google-cloud/secret-manager": "^5.6.0", + "@google-cloud/storage": "^7.14.0", + "@types/dompurify": "^3.0.5", + "@types/express": "^4.17.21", + "@types/google-protobuf": "^3.15.12", + "@types/hammerjs": "^2.0.45", + "@types/jimp": "^0.2.28", + "@types/msgpack5": "^3.4.6", + "@types/raphael": "^2.3.9", + "@types/twemoji": "^13.1.1", + "binary-loader": "^0.0.1", + "colord": "^2.9.3", + "crypto": "^1.0.1", + "d3": "^7.9.0", + "discord.js": "^14.16.3", + "dompurify": "^3.1.7", + "dotenv": "^16.4.7", + "express": "^4.21.1", + "google-auth-library": "^9.14.0", + "googleapis": "^143.0.0", + "hammerjs": "^2.0.8", + "ip-anonymize": "^0.1.0", + "jimp": "^0.22.12", + "lit": "^3.2.1", + "msgpack5": "^6.0.2", + "nanoid": "^5.0.9", + "node-addon-api": "^8.1.0", + "node-gyp": "^10.2.0", + "obscenity": "^0.4.3", + "priority-queue-typescript": "^1.0.1", + "protobufjs": "^7.3.2", + "pureimage": "^0.4.13", + "raphael": "^2.3.0", + "twemoji": "^14.0.2", + "uuid": "^10.0.0", + "wheelnav": "^1.7.1", + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "type": "module" }