Resolves#2823
## Description:
When playing in single-player mode, if an NPC reaches 80% land control
before the player, the game enters a broken state where:
- The game clock stops
- Win checking stops permanently
- Even if the player later conquers 100% of land, victory is never
awarded
- The game becomes "stuck" in a zombie state.
This PR addresses this allowing Nations to be set as winners in single
mode, and in this case showing a "Nation {nation} has won" modal to the
user. This WinModal is the same as the "{player} has won", with the only
change being the title.
Nation wins in FFA, from the human player perspective:
<img width="1457" height="837" alt="image"
src="https://github.com/user-attachments/assets/1ce569bd-6616-4a23-b4a4-afedad2c64f8"
/>
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
deshack_82603
## Pathfinding with HPA*
Hi! The primary objective of this PR is to replace per-tile A* with
hierarchical pathfinding - HPA*. In practice, this means we create an
abstract graph on top of the actual map with far fewer points and use it
to decide on general path structure. Only then we go back to tile-level
and build path between selected waypoints. This speeds up long distance
pathfinding by over 1000x in some cases. To make the review easier, it
comes with a benchmark and visual playground.
## PREPROCESSING
H part of HPA* means "hierarchical" and requires preprocessing.
This PR includes pre-processing as part inside `new Game()` constructor.
It takes about 135ms for `giantworldmap` on my machine, which increases
the effective initialization from ~95ms to ~230ms. This time could be
reduced in different ways, which are **out of scope** for this PR.
After confirming the initialization time is bearable on low-end devices,
I argue merging this PR as-is is acceptable tradeoff. It creates small
lag at the beginning of a round but pays for itself in the first minute
of the match.
## Nerdy details
**Architecture**
- HPA*-style hierarchical pathfinding
- 32×32 sectors on minimap with gateway nodes on borders
- Gateway graph built via BFS during preprocessing
- Water component optimization skips unreachable gateway pairs
- A* on gateway graph → local A* within sectors → Bresenham path
smoothing
- Minimap upscaling identical to currently used in MiniAStar
**Key Optimizations**
- Typed arrays instead of high-level primitives
- Stamp-based visited tracking (no need to recreate buffers, O(1)
clearing)
- Optional - enabled by default - caching of tile paths between gateways
- Line of sight smoothing for the final path
## Review Focus
Play with included tools, benchmark and visualization. Pathfinding
should be safe to merge as a black box - you do not need to understand
the details. Outcomes can be tested empirically in-game. Visualize (and
share!) edge cases with included playground. Confirm the 100x speedup is
real with benchmark.
If you plan to dive into the code, I suggest the following order:
- Pathfinding abstraction in `src/core/pathfinding/`
- Pathfinding tests in `tests/core/pathfinding/`
- NavMesh in `src/core/pathfinding/navmesh/` + integration with
`Game.ts`
- Benchmark in `tests/pathfinding/benchmark/`
Do not look at playground's code, it has been created with a clanker.
The design is 100% mine and I spent way too long polishing it, but I
haven't even once edited the code manually. There is probably no
abstraction whatsoever, just do not look at the code, let it play.
## Core Changes
#### Pathfinding (`src/core/pathfinding/navmesh/`)
- HPA* + refinement -> three phased pathfinding: A* over the graph ->
naive path -> refinement
- comes with A* and BFS optimized for for specific needs
#### Pre-Processing (`src/core/pathfinding/navmesh/`)
- identify water bodies to avoid pathfinding between disconnected nodes
- create high-level graph of gateways on top of tile map
#### Abstraction (`src/core/pathfinding/`)
- common `PathFinder` interface that can return full path and also act
as state machine (`.next()`)
- adapters for both new and legacy algorithm with fallback to legacy if
navigation mesh not available
#### Benchmark (`tests/pathfinding/benchmark/`)
- `npx tsx tests/pathfinding/benchmark/run.ts` - no guesswork, numbers
- `npx tsx tests/pathfinding/benchmark/run.ts --synthetic` - 1000s of
synthetic paths
- `npx tsc tests/pathfinding/benchmark/generate.ts` - generate more as
needed, test new maps
- includes ONE synthetic scenario to avoid PR bloat, generate more
locally / later
#### Playground (`tests/pathfinding/playground/`)
- `npx tsx tests/pathfinding/playground/server.ts` - visualize paths
with both new and legacy algorithm
## Benchmarks
### Compared with legacy in default - hand picked - scenario:
```
Initialization: 95.95ms -> 227.29ms
Pathfinding: 3038.43ms -> 6.45ms
Distance: 26972 -> 26810 tiles
```
### 42,000 synthetic routes across all maps
```
Running 42 synthetic scenarios with hpa.cached adapter...
✅ synthetic/achiran | Init: 93.42ms | Path: 139.07ms | Dist: 1481630 tiles | Routes: 1000/1000
✅ synthetic/africa | Init: 87.14ms | Path: 155.08ms | Dist: 1829414 tiles | Routes: 1000/1000
✅ synthetic/asia | Init: 57.60ms | Path: 112.55ms | Dist: 1204082 tiles | Routes: 1000/1000
✅ synthetic/australia | Init: 78.18ms | Path: 77.12ms | Dist: 978375 tiles | Routes: 1000/1000
✅ synthetic/baikal | Init: 78.26ms | Path: 152.14ms | Dist: 1600016 tiles | Routes: 1000/1000
✅ synthetic/baikalnukewars | Init: 81.44ms | Path: 165.90ms | Dist: 1699283 tiles | Routes: 1000/1000
✅ synthetic/betweentwoseas | Init: 29.29ms | Path: 114.99ms | Dist: 1338075 tiles | Routes: 1000/1000
✅ synthetic/blacksea | Init: 30.66ms | Path: 93.14ms | Dist: 949217 tiles | Routes: 1000/1000
✅ synthetic/britannia | Init: 74.12ms | Path: 85.62ms | Dist: 866752 tiles | Routes: 1000/1000
✅ synthetic/deglaciatedantarctica | Init: 105.49ms | Path: 192.93ms | Dist: 1574684 tiles | Routes: 1000/1000
✅ synthetic/didier | Init: 81.51ms | Path: 153.70ms | Dist: 1734876 tiles | Routes: 1000/1000
✅ synthetic/eastasia | Init: 49.29ms | Path: 128.63ms | Dist: 1410270 tiles | Routes: 1000/1000
✅ synthetic/europe | Init: 92.55ms | Path: 178.35ms | Dist: 1525216 tiles | Routes: 1000/1000
✅ synthetic/europeclassic | Init: 33.50ms | Path: 104.40ms | Dist: 1209759 tiles | Routes: 1000/1000
✅ synthetic/falklandislands | Init: 63.00ms | Path: 107.41ms | Dist: 1080251 tiles | Routes: 1000/1000
✅ synthetic/faroeislands | Init: 71.91ms | Path: 49.52ms | Dist: 604613 tiles | Routes: 1000/1000
✅ synthetic/fourislands | Init: 45.75ms | Path: 78.91ms | Dist: 937439 tiles | Routes: 1000/1000
✅ synthetic/gatewaytotheatlantic | Init: 81.00ms | Path: 257.06ms | Dist: 2555551 tiles | Routes: 1000/1000
✅ synthetic/giantworldmap | Init: 214.25ms | Path: 220.42ms | Dist: 1976693 tiles | Routes: 1000/1000
✅ synthetic/gulfofstlawrence | Init: 45.16ms | Path: 96.05ms | Dist: 1014604 tiles | Routes: 1000/1000
✅ synthetic/halkidiki | Init: 74.68ms | Path: 149.39ms | Dist: 1546781 tiles | Routes: 1000/1000
✅ synthetic/iceland | Init: 58.72ms | Path: 78.16ms | Dist: 1001554 tiles | Routes: 1000/1000
✅ synthetic/italia | Init: 29.78ms | Path: 139.93ms | Dist: 1412024 tiles | Routes: 1000/1000
✅ synthetic/japan | Init: 161.07ms | Path: 118.65ms | Dist: 1154393 tiles | Routes: 1000/1000
✅ synthetic/lemnos | Init: 52.59ms | Path: 136.69ms | Dist: 1481101 tiles | Routes: 1000/1000
✅ synthetic/lisbon | Init: 49.27ms | Path: 86.53ms | Dist: 1032011 tiles | Routes: 1000/1000
✅ synthetic/manicouagan | Init: 53.74ms | Path: 110.52ms | Dist: 1307630 tiles | Routes: 1000/1000
✅ synthetic/mars | Init: 29.39ms | Path: 80.55ms | Dist: 1091702 tiles | Routes: 1000/1000
✅ synthetic/mena | Init: 26.37ms | Path: 120.09ms | Dist: 1272751 tiles | Routes: 1000/1000
✅ synthetic/montreal | Init: 26.08ms | Path: 106.77ms | Dist: 1187736 tiles | Routes: 1000/1000
✅ synthetic/newyorkcity | Init: 56.60ms | Path: 181.19ms | Dist: 1753875 tiles | Routes: 1000/1000
✅ synthetic/northamerica | Init: 96.29ms | Path: 123.02ms | Dist: 1217221 tiles | Routes: 1000/1000
✅ synthetic/oceania | Init: 52.81ms | Path: 51.96ms | Dist: 482373 tiles | Routes: 1000/1000
✅ synthetic/pangaea | Init: 21.29ms | Path: 56.58ms | Dist: 716189 tiles | Routes: 1000/1000
✅ synthetic/pluto | Init: 53.89ms | Path: 141.62ms | Dist: 1304362 tiles | Routes: 1000/1000
✅ synthetic/southamerica | Init: 85.19ms | Path: 123.03ms | Dist: 1301403 tiles | Routes: 1000/1000
✅ synthetic/straitofgibraltar | Init: 76.68ms | Path: 108.30ms | Dist: 1304592 tiles | Routes: 1000/1000
✅ synthetic/straitofhormuz | Init: 38.97ms | Path: 67.78ms | Dist: 754920 tiles | Routes: 1000/1000
✅ synthetic/surrounded | Init: 95.35ms | Path: 90.18ms | Dist: 1017142 tiles | Routes: 1000/1000
✅ synthetic/svalmel | Init: 60.58ms | Path: 104.75ms | Dist: 1235501 tiles | Routes: 1000/1000
✅ synthetic/twolakes | Init: 62.05ms | Path: 94.54ms | Dist: 1140807 tiles | Routes: 1000/1000
✅ synthetic/world | Init: 41.43ms | Path: 93.42ms | Dist: 873406 tiles | Routes: 1000/1000
Completed 42 scenarios
Total Initialization Time: 2796.32ms
Total Pathfinding Time: 5026.64ms
Total Distance: 53160274 tiles
```
## Playground
**That's the fun part**. Watch NavMesh running circles around legacy
`PathFinder.Mini` in real time. Debug inner workings, test edge cases,
share URLs for debugging.
https://github.com/user-attachments/assets/34e2e3f5-fbc1-4b1f-917d-820766e98d5d
## Discord Tag
`moleole`
## Description:
Previously, the zod schemas for troop and gold donation allowed for
negative values which could open the game up to vulnerabilities through
undefined behavior in the future. We mitigate these vulnerabilities but
adding `.nonnegative` to the `DonateGoldIntentSchema` and
`DonateTroopIntentShcema` respectively. Today, code exists to prevent
this deeper in the codebase, but we should also prevent this earlier if
possible during intent validation.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
haticus
## Description:
Added a public game modifier system. It causes that
5% of public games are played on the compact version of the map
10% of public games have "Random Spawn" activated
Percentages can easily get changed via `DefaultConfig`.
We can also easily add more modifiers.
Modifiers can stack, so in rare cases you will play on a compact map
with random spawn 😄
More variety!
### "Compact Map" modifier implementation
- With the "Compact Map" modifier the lobby max player count gets
reduced to 25% and only 25% of the regular bots and only 25% of the
regular nations will spawn (because the map has only 25% of its regular
size)
- In private lobbies and singleplayer the nation reduction happens too
(When "Compact Map" is enabled).
### Restrictions
- Duos/Trios/Quads team modes do not get Random Spawn (defeats the
purpose)
- Maps with smallest player count < 50 do not get Compact Map in team
games (not enough players after the reduction to 25%). I have calculated
all the possible max player counts.
### How it looks like
Random Spawn modifier:
<img width="528" height="183" alt="Screenshot 2026-01-06 194959"
src="https://github.com/user-attachments/assets/2f729da9-80c3-4548-8205-71129da2a76a"
/>
Very rare case: Two modifiers at the same time and only 10 max players
have been chosen from `[GameMapType.FaroeIslands]: [20, 15, 10]`.
Because of the 75% reduction in player count only 3 players are allowed
(3 is the minimum). I think its funny that you can play a 1v1v1 in rare
occasions 😄
<img width="526" height="184" alt="Screenshot 2026-01-06 194938"
src="https://github.com/user-attachments/assets/834326eb-df03-41b7-b1db-1efa3f1013b5"
/>
### Funny side-effect
Team games with random spawn. That will be interesting. No more "Who is
better in donating troops to the frontline". Instead you have to heavily
coordinate with your teammates.
## Please complete the following:
- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [X] I have added relevant tests to the test directory
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
FloPinguin
## Description:
Resolve discussions about stalled PR
https://github.com/openfrontio/OpenFrontIO/pull/2460
<img width="724" height="348" alt="image"
src="https://github.com/user-attachments/assets/c2c9fa79-cace-431a-9ca4-b3656612fa9d"
/>
Changes:
- Added a `Player::canAttackPlayer(other)` function to determine whether
a player can be attacked.
- This function is now used in most places where a fight can occur:
- AttackExecution (land attacks)
- Naval invasion
- Warship fight
- Nukes can't be thrown during the truce
- Immunity only affect human players. Nations and bot will fight as
usual, and can be fought against.
- The immunity timer uses minutes in the modal window.
UI:
- The immunity phase is displayed with a timer bar at the top. This is
from the original PR, to be discussed if it's not deemed visible enough:
<img width="632" height="215" alt="image"
src="https://github.com/user-attachments/assets/f5ab9aa0-bd4f-4503-b8d6-b40b121fba65"
/>
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
IngloriousTom
---------
Co-authored-by: newyearnewphil <git@nynp.dev>
Fixes#2758
## Description:
This PR migrates lobby configuration updates from the HTTP PUT
`/game/:id` endpoint to a WebSocket-based intent flow.
The lobby creator is already authenticated via the game WebSocket, so
updating configuration through intents removes redundant authentication
and aligns with existing real-time lobby actions such as `kick_player`
and `toggle_pause`.
## Changes Made
- Added `update_game_config` WebSocket intent schema
- Wired client → transport → server intent handling
- Refactored `putGameConfig()` to emit WebSocket intent instead of HTTP
fetch
- Preserved all existing validation, partial-update semantics, and
client-side debouncing
- Left the REST endpoint untouched for backward compatibility
## Testing
- All existing automated tests pass
- Manual verification completed:
- Lobby creator can update all lobby settings
- Non-creators are rejected
- Updates are rejected after game start
- Bots slider debounce (300ms) remains intact
- No `PUT /api/game/:id` requests are made from the lobby UI
## Checklist:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Description:
The sync-assets wasn't executing on docker-build. so instead just import
it from resources/ directory, vite logs a warning but I think that's
okay for now.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
Resolves#2434
## Description:
Allows bomb direction to be inverted by pressing a hotkey - currently
"U".
**Check the issue for screenshots / videos.**
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
w.o.n
---------
Co-authored-by: Evan <evanpelle@gmail.com>
Co-authored-by: iamlewis <lewismmmm@gmail.com>
- Replace Webpack with Vite for faster client bundling and HMR.
- Migrate tests from Jest to Vitest and update configuration.
- Update Web Worker instantiation to standard ESM syntax.
- Implement Env utility in `src/core` for safe, hybrid environment
variable access (Vite vs Node).
- Refactor configuration loaders to remove direct `process.env`
dependencies in shared code.
- Update TypeScript environment definitions and project scripts for the
new toolchain.
- Remove the [depracated usage of the
husky](https://github.com/typicode/husky/releases/tag/v9.0.1).
## Description:
migrate build system to Vite and test runner to Vitest & Remove
depracated husky usage
## Please complete the following:
- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [ ] I have added relevant tests to the test directory
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
wraith4081
---------
Co-authored-by: evanpelle <evanpelle@gmail.com>
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves#2491
## Description:
Adds pause/unpause functionality for private multiplayer games. Only the
lobby creator can pause the game, and all players see a pause overlay
when the game is paused.
**Key features:**
- Lobby creator sees pause/play button in control panel (alongside
existing singleplayer/replay controls)
- Server validates that only lobby creator can toggle pause
- All players see "Game paused by Lobby Creator" overlay when paused
- Game state freezes (no turn execution) while paused
- Unpause resumes normal gameplay
**Implementation details:**
- Server-side pause state (`isPaused`) prevents turn execution during
pause
- Each client receives `isLobbyCreator` flag in `GameStartInfo` to
show/hide pause button
- Added `TogglePauseIntent` that broadcasts to all clients via
`NoOpExecution`
- New `PauseOverlay` component (shows in single player also)
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
furo18
<img width="1459" height="861" alt="Screenshot 2025-12-20 at 15 16 33"
src="https://github.com/user-attachments/assets/f5a3222f-f54b-473c-b0f6-104ce4c1e7a8"
/>
## Description:
1. Using the wording `"Nation"`, `"FakeHuman"` and `"NPC"` at the same
time is confusing.
So I renamed every mention of `"FakeHuman"` and `"NPC"` in the entire
project to `"Nation"`. Just like they are called ingame.
2. `BotBehavior.ts` was originally intended for sharing the logic
between nations and bots.
But at the moment, the logic there isn't really shared and it's
basically just about attacking.
So I renamed `BotBehavior.ts` to `AiAttackBehavior.ts`. I use "Ai" to
indicate that this file is used by bots AND nations.
3. Moved `execuction/utils/AllianceBehavior.ts` to
`execuction/nation/NationAllianceBehavior.ts` to make sure everybody
understands that this file is not about alliances in general. It's just
about nations and how they handle alliances.
4. Removed `difficultyModifier` from `DefaultConfig`. It's unused and I
think we usually want to finetune the difficulty instead of using that
method.
5. Added `assertNever` in all `switch (difficulty)` default cases.
## Please complete the following:
- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [X] I have added relevant tests to the test directory
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
FloPinguin
## Description:
This PR centralizes all username validation using UsernameSchema with a
set maximum, minimum, and a regex pattern,
It also removes sanitization, as all places where the username would be
sanitized on the server have been gatekept, so no unvalidated usernames
can get onto the server past the ClientMessageSchema safeParse in
GameServer's on message func.
Here is how the errors look if that happens, Note that if the client is
funtioning correctly and the user doesn't manually send a WS message,
they should never see this. The screenshots are from a debug build where
client uname validation was disabled.
<img height="300" alt="error message too short"
src="https://github.com/user-attachments/assets/1b7ac32c-2f03-40fb-8ce9-1f4ab66100bd"
/>
<img height="300" alt="error message bad regex"
src="https://github.com/user-attachments/assets/c78b4114-7e4b-4d39-a135-4cab3ad52c0b"
/>
Profanity sanitization was not changed.
Additionally, the censor tests were updated to reflect the new
expectations.
Jose was added to the jest config as an allowed transform pattern, as it
didn't make sense to me to mock a zod schema.
The UsernameSchema pattern was set to `^[a-zA-Z0-9_ \[\]üÜ]+$`, I
personally think either we should allow all latin characters (regex has
a pattern for this, `\p{L}` or `\p{sc=Latin}`) and then we'd use some
kind of library to normalize all latin characters into regular ascii for
name filtering, or we should only keep ascii letters.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
Lavodan
(I just realized sanitization isn't a word, it's supposed to be
sanitation, sorry.)
When user tries to join either a public or private multiplayer game, the
turnstile callback is triggered, and the turnstile token is passed to
the server when joining a game.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
Previously, the connection and reconnection logic were identical in
Worker.ts, so clients would need to be re-authorized for cosmetics etc
even when reconnecting. Now, on reconnect, Worker.ts only does
authentication - verifying the jwt is valid.
This will allow clients to require a valid turnstile token when first
connecting, and not when reconnecting after a broken ws connection.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
Finishes & closes#1969 and closes#1806.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
w.o.n
---------
Co-authored-by: Evan <evanpelle@gmail.com>
## Description:
Fixes: #676
This PR adds Players Vs Nations as a game mode in the menu.
For this change I have added two mutually exclusive option for this
mode:
1. Match number of nations to number of players who have joined
2. Set the number of nations to a fixed value
### Screenshots:
#### Options in Single player mode
<img width="1025" height="790" alt="image"
src="https://github.com/user-attachments/assets/c0685ea5-94f5-43c7-a9e5-390835fc94e9"
/>
<img width="1005" height="795" alt="image"
src="https://github.com/user-attachments/assets/dddba015-a424-40dd-a0fe-2571fd7b0fba"
/>
#### Options in lobby mode
<img width="1015" height="888" alt="image"
src="https://github.com/user-attachments/assets/45bc865b-c6a8-4b6a-9062-4eb499c1ea36"
/>
#### Example gameplay (1 Human Vs 90 Nations)
<img width="1888" height="912" alt="image"
src="https://github.com/user-attachments/assets/38faec75-171f-4358-a3be-93630cca1587"
/>
## Please complete the following:
- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [X] I have added relevant tests to the test directory
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
saphereye
---------
Co-authored-by: Evan <evanpelle@gmail.com>
## Description:
This will be used to determine clan winners in the api layer.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
fixes#2275
Added global Start/Stop trading; use your **player panel** to trigger
it.
<img width="370" height="540" alt="Screenshot 2025-10-23 184447"
src="https://github.com/user-attachments/assets/c3b7967e-ffdd-4f37-ba67-b60a602278ce"
/>
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
regression is found:
abodcraft1
## Description:
fixes#2275
Added global Start/Stop trading; use your **player panel** to trigger
it.
<img width="370" height="540" alt="Screenshot 2025-10-23 184447"
src="https://github.com/user-attachments/assets/c3b7967e-ffdd-4f37-ba67-b60a602278ce"
/>
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
abodcraft1
## Description:
Adds a max timer setting
The timer starts at max timer and goes down, becoming red if reaching <
1 min
The player with the biggest territory wins at the end of the timer

## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors
## Please put your Discord username so you can be contacted if a bug or
regression is found:
Vivacious Box
---------
Co-authored-by: Loymdayddaud <145969603+TheGiraffe3@users.noreply.github.com>
## Description:
Added a colors tab in territory patterns modal so players can select
their color.
Refactored the PrivilegeChecker, removed custom flag checks since we no
longer support custom flags.
<img width="479" height="345" alt="Screenshot 2025-09-27 at 5 01 17 PM"
src="https://github.com/user-attachments/assets/ad96da65-f0eb-4731-a861-e6e5fcb4566a"
/>
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
### ✅ Summary of Changes
This PR enables the ESLint rule **`@typescript-eslint/no-unused-vars`**
as requested in the issue and applies the necessary code adjustments
across the project.
#### 🔧 What was done:
- Activated the rule `@typescript-eslint/no-unused-vars` in the ESLint
config.
- Updated ~70 files to comply with the rule:
- Replaced unused variables with a `_` prefix where appropriate.
- Added inline ESLint disable comments (`eslint-disable-next-line`) for
specific cases where the variable or code block seemed important for
context, readability, or future use.
- Ensured no linting errors remain related to this rule.
---
### ❓ Clarification
Some cases were handled with inline disable comments instead of removing
the variable entirely, to avoid accidental breaking changes or loss of
intent.
If a different approach is preferred (e.g., stricter removal or
alternative handling), I’m happy to adjust the implementation
accordingly — just let me know!
---
### 🙌 Next Steps
Please review and let me know if:
- Any file should be handled differently.
- You prefer removal instead of disabling in certain areas.
- Additional rules should be enforced or reverted.
I’m available to make any follow-up improvements needed.
---
### 🎃 Hacktoberfest Note
I'm participating in **Hacktoberfest**, so if this PR is accepted,
please add the label:
`hacktoberfest-accepted`
Thank you!
#1784
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
DISCORD_USERNAME
## Description:
Create mini map option
<img width="741" height="234" alt="Screenshot 2025-09-25 at 4 47 47 PM"
src="https://github.com/user-attachments/assets/6c442698-8e3b-44d5-b07e-c4f0a916c3bc"
/>
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
Bug: Donation to nations was not allowed in single player mode.
Always allow donation to Nations. Simplify the donation check logic.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
Add support for colored territory patterns/skins
* Refactored & updated territory pattern rendering to render colored
skins
* rename public from pattern to skin (keep pattern name internally, too
difficult to rename)
* Moved all territory color logic to PlayerView
* Updated WinModal to show colored skins
* Refactored decode logic into a separate function: decodePatternData
* Refactored/updated how cosmetics are sent to server. Players now send
a PlayerCosmeticRefsSchema in the ClientJoinMessage.
PlayerCosmeticRefsSchema just contains names of the cosmetics, and the
server replaces the names/references with actual cosmetic data
* Refactored PastelThemeDark: have it extend Pastel theme so duplicate
logic can be removed.
*
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
Describe the PR.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
This removes the dependencies on R2, and allows contributors to replay
games without R2 access.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
Added a red delete button with trash can icon to the right-click radial
menu that allows players to voluntarily delete their own units.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I have read and accepted the CLA agreement (only required once).
## Please put your Discord username so you can be contacted if a bug or
regression is found:
Kipstz
<img width="286" height="209" alt="image"
src="https://github.com/user-attachments/assets/85142be3-2aa5-4c84-ab30-0c68289c8f85"
/>
---------
Co-authored-by: Drills Kibo <59177241+drillskibo@users.noreply.github.com>
## Description:
Store pattern by name instead of value. The worker replaces the pattern
name with it's base64 when joining. This ensures the client & server are
never out of sync after patterns are updated.
* removed resizeObserver on the territory modal, it was causing some
race conditions, and the modal is not resizable so it's unnecessary.
* Moved PatternSchema to CosmeticSchema
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I have read and accepted the CLA agreement (only required once).
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
Some flags are offensive, so don't allow them to be selected by users.
I think it's fine for nations continue to use the flags.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
PersistentIdSchema.safeParse(v).success returns a boolean not
undefined/null.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I have read and accepted the CLA agreement (only required once).
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
#1075
Fixing all remaining type errors caused by strict mode and enable it.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I have read and accepted the CLA agreement (only required once).
## Please put your Discord username so you can be contacted if a bug or
regression is found:
azlod
---------
Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
## Description:
The troop/worker ratio bar is almost never changed. so remove it and the
entire concept of workers. Now there is just troops.
Now players get a consistent 1k/s gold.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I have read and accepted the CLA agreement (only required once).
## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
## Description:
Added player management features so lobby hosts can kick players from
private games. This includes both UI changes and backend work.
### What's new:
- Hosts can now kick players from private lobbies with a simple button
- Added host badges and remove buttons to the UI
- Made sure only hosts can kick people, and hosts can't kick themselves
### How it works:
- When someone creates a private game, they automatically become the
host
- Kicking happens through WebSocket "kick-player" events
- Server checks that you're actually the host before letting you kick
anyone
<img width="1291" height="871" alt="Screenshot 2025-07-15 002114"
src="https://github.com/user-attachments/assets/ea575f83-a0f4-45d1-9cfe-7521d373f3d5"
/>
### Known Issues:
- Kicked player gets general message (same when kicked for multi tab)
### Other Issues:
- Host abandoment still existent (host clicks on x; or is closing tab)
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I have read and accepted the CLA agreement (only required once).
## Please put your Discord username so you can be contacted if a bug or
regression is found:
[UN]nvm
---------
Co-authored-by: floriankilian <floriankilian@users.noreply.github.com>
## Description:
Fixes#1554 (short description)
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I have read and accepted the CLA aggreement (only required once).
## Please put your Discord username so you can be contacted if a bug or
regression is found:
dovg
## Description:
This contains the update from zod 3.25 to 4. Change imports back from
zod/v4 to just zod. No errors in VSCode.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I have read and accepted the CLA aggreement (only required once).
## Please put your Discord username so you can be contacted if a bug or
regression is found:
tryout33
## Description:
Enforce valid tile during spawn, to prevent the game from crashing for
all players.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [ ] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [ ] I have read and accepted the CLA aggreement (only required once).
## Description:
Attempt to fix bigint serialization issue
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [ ] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [ ] I have read and accepted the CLA aggreement (only required once).
## Description:
Modified the regex pattern & full unicode support (/u)
1) Allow Umlaute "üÜ" in usernames on client-side
2) Allow Umlaute "üÜ" in usernames on server-side
Before only the name "üÜüÜ" would have been possible as the pattern was
|üÜ -> or "üÜ" as a combined sequence
|[üÜ] -> or either one of these letters
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [ ] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I have read and accepted the CLA aggreement (only required once).
Not 100% sure what/where else to test. I started couple of private
lobbies with various names.
## Please put your Discord username so you can be contacted if a bug or
regression is found:
[UN nvm]
## Description:
Enable a few eslint rules:
- `@typescript-eslint/no-empty-object-type`
- `@typescript-eslint/no-require-imports`
- `no-useless-escape`
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors
---------
Co-authored-by: Scott Anderson <scottanderson@users.noreply.github.com>
## Description:
[issue](https://github.com/openfrontio/OpenFrontIO/issues/1367)
<img width="466" alt="スクリーンショット 2025-07-09 21 19 07"
src="https://github.com/user-attachments/assets/e1a1ffcf-2440-4b27-b540-242044fbd002"
/>
<img width="926" alt="スクリーンショット 2025-07-09 21 08 27"
src="https://github.com/user-attachments/assets/2f7d4edd-62db-478f-8756-e59e693b1354"
/>
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors
## Please put your Discord username so you can be contacted if a bug or
regression is found:
aotumuri
## Description:
Add domain, subdomain to GameRecord.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors
Co-authored-by: evanpelle <evanpelle@gmail.com>
## Description:
- Add trios and quads
- Add translations for duos, trios, quads
- Add duos, trios, quads to the public lobby rotation
- Increase the frequency of team games


## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors