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"
}