Commit Graph

190 Commits

Author SHA1 Message Date
evanpelle a2eae98d6b revert 2026-05-03 22:34:00 -06:00
evanpelle 5d6c48ebf2 Revert "Fix winner stats spoofing exploit"
This reverts commit 819edb21bb.
2026-05-03 09:54:50 -06:00
evanpelle 819edb21bb Fix winner stats spoofing exploit
Previously, winnerVotes was keyed only on winner, so a client could send the correct winner with spoofed allPlayersStats and have their vote count toward the majority. The key is now a SHA-256 hash of both winner and allPlayersStats together, so any stats divergence produces a distinct key that must independently reach majority.

When the winner is confirmed, the log now includes votesByKey — a summary of every distinct key, its vote count, and winner value — making stat manipulation visible in the logs.
2026-05-02 13:47:19 -06:00
Evan 2994a5f848 Start game via WebSocket intent (#3794)
## Description:

Replaces the HTTP POST /api/start_game/:id endpoint with a WebSocket
intent, making private game start consistent with how kick_player and
update_game_config already work. Also verifies that only the lobby
creator can start 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
2026-04-29 13:26:14 -06:00
Ryan c3d7d0373e Improve ingame moderation for admins (#3678)
## Description:

Players with the `admin` flare can now kick players from any game
(including public lobbies), not just the lobby creator in private
lobbies.

## 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
2026-04-20 11:09:04 -07:00
babyboucher e0cc50d3c4 Fix hostCheats check not disabling (#3691)
## Description:

Currently if you deselect Host cheats, any active host cheats will still
be active. This fixes it so if you deselect Host cheats, all the cheats
are disabled.

## 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:

babyboucher
2026-04-15 16:55:06 -07:00
FloPinguin 9821e8e041 Add host cheats for streamers (Specifically Enzo) (#3671)
## Description:

- Adds a "Host Cheats" toggle in the private lobby options section that
reveals a dedicated section with four host-only cheats: infinite gold,
infinite troops, gold multiplier, and starting gold
- Only the lobby creator receives the cheat effects in-game (checked via
`isLobbyCreator` in DefaultConfig)
- Joining players see active host cheats displayed as yellow badges in
the lobby UI
- Adds `hostCheats` optional object to `GameConfigSchema` and wires it
through the server config update whitelist
- Raises the intent size limit for `update_game_config` messages
(lobby-only, not stored in turn history) to prevent rate-limiter kicks
(I always got too-much-data-kicked after selecting "host cheats" lol)

<img width="861" height="525" alt="image"
src="https://github.com/user-attachments/assets/51e51ec4-c2e8-46ca-b258-11a93487964f"
/>


<img width="933" height="825" alt="image"
src="https://github.com/user-attachments/assets/5acbd38d-2097-42e1-ba78-0fb17d6afe82"
/>

## 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
2026-04-15 15:20:08 -07:00
babyboucher 17f9cd3147 Fix public lobby max player bypass (#3650)
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves #3649

## Description:

Removes the persistentIdToClientId for Clients that leave before the
game starts. This prevents the rejoin path from being taken which
ignores max player count. See issue for details on why this is
important.

## 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:

babyboucher
2026-04-12 14:00:46 -07:00
FloPinguin 7f7cbba12f Water-Nukes 💧 (#3604)
## Description:

Adds a new `waterNukes` game config option that causes nuclear
detonations to convert land tiles into water instead of just leaving
fallout. When enabled, nuked land tiles are batched and converted to
water each tick, with full terrain metadata updates including:

- Ocean bit propagation from adjacent ocean tiles (BFS flood fill)
- Magnitude recomputation via BFS from remaining coastlines
- Shoreline bit fix-up in a 2-ring neighborhood around converted tiles
- Minimap terrain sync (majority-rule downsampling)
- Throttled water navigation graph rebuild (every 20 ticks) for ship
pathfinding
- Ship executions detect graph rebuilds and refresh their pathfinders
- TransportShips auto-retreat if their destination becomes water
- Water nuke craters use a smoothed angular noise ring with a
bounding-box scan instead of the regular per-tile random coin flip with
BFS, producing clean blob-shaped craters without scattered land pixels
that players would have to boat to individually

The `TerrainLayer` now incrementally repaints tiles that changed terrain
type, and tile update packets encode the terrain byte alongside tile
state so clients can reflect water conversions in real time.

When `waterNukes` is disabled, behavior is unchanged (fallout only).

Includes a new test suite (WaterNukes.test.ts) covering the conversion
pipeline, ocean propagation, magnitude recalculation, shoreline updates,
and minimap sync.

Also adds a new public game modifier for the special rotation.

### The only problem
A bit of lag on impact. But otherwise it works great and is fun. Maybe
needs some followup improvements if it gets merged.
I think its very cool in baikal / four islands team games. Chip away the
territory of your opponents.
Its also fun to turn The Box / Alps into a water map (its actually
possible to boat-trade then)

### Media

Video does not show the updated craters


https://github.com/user-attachments/assets/aed8bf08-0e94-4484-b997-4de11ae313d9

Updated craters (no tiny islands after impact):

<img width="1920" height="1080" alt="image"
src="https://github.com/user-attachments/assets/e896870b-bc9d-493d-8bc8-b3a5427d69d3"
/>

<img width="1472" height="920" alt="image"
src="https://github.com/user-attachments/assets/677065aa-0159-48cd-af44-a91b0f57adfc"
/>

<img width="1296" height="892" alt="image"
src="https://github.com/user-attachments/assets/886ffaba-541f-4e46-97c6-ce963f632fe0"
/>

## 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
2026-04-08 20:56:02 -07:00
FloPinguin 50bd075b1c Fix deselected host lobby settings persisting for joiners 🐛 (#3607)
## Description:

### Problem

When a host toggled off certain settings (game length, PVP immunity,
starting gold, gold multiplier, disable alliances) in the host lobby
modal, joiners still saw the old values. The settings appeared "stuck"
once enabled.

### Root Cause

`putGameConfig()` sent `undefined` for disabled settings, but
`JSON.stringify` strips `undefined` properties entirely. The server's
`!== undefined` guard never fired, so the old value was never cleared.

### Fix

- **HostLobbyModal**: Send `null` instead of `undefined` when these
settings are toggled off (`null` survives JSON serialization)
- **Schemas**: Add `.nullable()` to the five affected fields
(`maxTimerValue`, `spawnImmunityDuration`, `goldMultiplier`,
`startingGold`, `disableAlliances`)
- **GameServer**: Use `?? undefined` (nullish coalescing) to convert
incoming `null` back to `undefined` when storing on the config

Other settings are unaffected. Booleans like `infiniteGold` always send
`true`/`false`, and fields like `bots`/`gameMap` always have a concrete
value..

## 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

---------

Co-authored-by: Evan <evanpelle@gmail.com>
2026-04-07 11:51:42 -07:00
FloPinguin 0dc522413e Close private lobby when host leaves 🚪 (#3503)
## Description:

When the host of a private lobby disconnects before the game starts, the
lobby is now closed:

- **Server:** Detects host disconnection via `creatorPersistentID`
match, kicks all remaining clients with a `host_left` reason, and marks
the game as ended so it's cleaned up by the game manager and can no
longer be joined.
- **Client:** Participants receive an `alert()` saying "The host has
left the lobby." and their JoinLobbyModal is closed automatically via
the `leave-lobby` event.
- **Phase logic:** `phase()` now returns `Finished` for ended private
lobbies that haven't started, preventing the game from lingering in
`Lobby` state.

## 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
2026-03-24 13:00:20 -07:00
Ryan 6e67c2bf0d visibleAt (#3497)
## Description:

 needs prereq of https://github.com/openfrontio/infra/pull/272

## 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
2026-03-24 12:53:32 -07:00
Ryan 1049b7e7dc Clan System Part 1 (#3276)
## Description:

Properly split out clantags and usernames, a clantag should not be part
of a username.

<img width="285" height="286" alt="image"
src="https://github.com/user-attachments/assets/8ac56e82-b12c-4fc0-9774-e445252a6e61"
/>

https://api.openfront.dev/game/ojkqZFb2


<img width="296" height="596" alt="image"
src="https://github.com/user-attachments/assets/85152f80-c111-4f87-b85b-8516c9c6137b"
/>


https://api.openfront.dev/game/MF32BkVc


requires;
https://github.com/openfrontio/infra/pull/264

## 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
2026-03-17 15:55:47 -07:00
Evan 5fb7f75f3d Server-side WebSocket message rate limiting & size enforcement (#3424)
## Description:

* Adds ClientMsgRateLimiter — a per-client token-bucket rate limiter
that gates all incoming WebSocket messages. Returns "ok", "limit"
(drop), or "kick" based on the violation type.

* Intent messages are capped at 500 bytes each (they are stored in turn
history for the game duration, so oversized intents
accumulate in server RAM). Violations kick the client.

* Winner messages bypass the byte rate limit (they include stats for all
players and can be 100s of KB) but are strictly capped at one per client
— a second winner message kicks the client.

* All other messages go through the standard per-second (10/s) and
per-minute (150/min) rate limits. Violations drop the message; byte
budget exhaustion kicks the client.

* WebSocket maxPayload set to 2 MB on game workers.
Invalid (unparseable) messages now immediately kick the client rather
than being silently dropped.
Unit tests added for all rate limiting behaviors.

## 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
2026-03-13 21:15:10 -07:00
evanpelle 2741e315e0 kick players when intents too large 2026-03-13 18:44:18 -07:00
evanpelle d4321ba81f add intent based rate limits 2026-03-13 18:32:29 -07:00
evanpelle ebe7f76cd3 fix: track max player count reached to correctly determine lobby phase, this is to prevent old lobbies reappearing when someone leaves before the game starts 2026-03-12 11:25:02 -07:00
evanpelle 4cfefdfb02 fix: don't report game as Lobby phase when players leave mid-game
After a player left, the server would fall back to Lobby phase if player
count dropped below the max — even if the game had already started.
Now checks hasStarted() before reporting Lobby phase.
2026-03-11 21:30:04 -07:00
evanpelle fee625801d fix: track desynced clients as live set instead of accumulating count 2026-03-11 14:35:25 -07:00
FloPinguin 3838de1d30 Option to disable alliances + 2 new modifiers for variety 😄 (#3392)
## Description:

Rex had this idea: "It would be funny to have an option in private
lobbies to disable alliances."
I added it as an option.
Now people can choose to live in constant fear of their neighbors 😆 

Also added two new public game modifiers for variety (only for the
special rotation):
- Alliances disabled (low probability)
- x2 gold multiplier (low probability)

Would be nice to squeeze this into v30, last minute?

## 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
2026-03-09 21:13:13 -07:00
FloPinguin 0b9d43cb46 Configurable nation count 🤖 (#3338)
## Description:

I hope we can get this into v30?
The nation count is configurable now, just like the bot count.
Replaced the "Disable Nations" toggle with a nations slider (0–400) in
SinglePlayer and Host Lobby modals.

<img width="710" height="121" alt="Screenshot 2026-03-03 021952"
src="https://github.com/user-attachments/assets/c8d0f0c3-db51-4303-95fa-dbc770460ec2"
/>


Public games are staying exactly the same, this is just for singleplayer
and private lobby fun.
Youtubers could play HvN against 400 nations, for example.
Singleplayer enjoyers no longer have to play against 1 nation in HvN,
they can freely choose.

`GameConfig.disableNations: boolean` got replaced by `nations: number
(0-400, optional)`
`undefined` = map default, 
`0` = disabled, 
number = custom count

Nations slider defaults to the map's nation count, shows "(MAP DEFAULT)"
label when unchanged
Compact map toggle reduces nations to 25% when at default, restores when
toggled off (just like we already do with bots)
The nation count for HvN no longer automatically matches the human count
in singleplayer and private games, only in public games.

**What if there aren't enough nations configured for the map?**
We just use the HvN logic (Generate random nations)

### Warning

**This infra PR also needs to get merged:
https://github.com/openfrontio/infra/pull/263
Otherwise players can set 0 nations and get achievements.**

## 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
2026-03-03 14:07:06 -08:00
Ryan aa451e217f [BUGFIX] allow users to update username pre-game (#3298)
## Description:

Fix player rename in pre-game lobby on rejoin

Previously, when a player left a lobby, changed their name, and
rejoined, the server reused the original Client object without updating
the username. Now rejoinClient accepts the new username and applies it
if the game hasn't started yet, while still preserving names mid-game
for consistency.


## 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
2026-02-28 22:22:10 +00:00
Evan 90978c0e92 bugfix: set lobby start time only when it's the next lobby in rotation (#3261)
## Description:

The master set lobby start times on creation, which caused an issue if
the previous lobby filled up and started before its timer ran out, the
next lobby would have its timer set too far back. For example, if lobby
time is 60 seconds, and the first lobby fills up after 10s, the
subsequent lobby would have its timer set for 110 seconds (60+50).

Instead we have the master set the lobby start time only when it is next
up in rotation. So all lobbies behind it don't have a start time,
because we don't actually know what it should be.

## 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
2026-02-21 21:08:33 -06:00
evanpelle f7ebde1c7f reduce error log spam: don't error on client id mismatch and don't log abort in worker matchmaking polling 2026-02-18 15:42:00 -06:00
Evan 2e2e686699 have lobby schedule ffa, teams, & special game types (#3196)
## Description:

This implements the backend for multiple lobbies in preparation for
https://github.com/openfrontio/OpenFrontIO/pull/3191

The server now schedules & sends a map of game type (ffa, teams,
special) => public lobbies.

NOTE: this is just temporary, the lobby only shows ffa currently.

Have the Master scheduler schedule ffa, teams, & special 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

## Please put your Discord username so you can be contacted if a bug or
regression is found:

evan
2026-02-14 11:59:35 -08:00
Ryan 8dcc7cfb9a Fix client reconnection after page refresh (#3117)
## Description:

- Removed all code related to generating a client ID on the client. The
server now assigns the client ID and sends it to the client in lobby
messages.

## 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
2026-02-09 01:10:11 +00:00
Evan 294a1b4784 move lobby websockets to worker (#2974)
## Description:

Currently only the master process sends public lobby updates to clients.
This is not scalable since it could overload the master process.

In this PR, the master uses IPC to send public lobby info to all
workers. Then clients connect to a random worker to get public lobby
updates via websocket. This way clients never connect directly to the
master websocket.

The flow looks like this:

Every 100ms:
1. Master schedules a public game on a random worker if new games are
needed
2. Master broadcasts public lobby info to all workers (all public games
& num clients connected to each game)
3. Each worker responds to that update with the number of clients
connected to its own public games
4. Master then updates its public lobby state so it knows how many
clients are connected to each public 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
2026-02-03 18:26:38 -08:00
Ryan 2baaebfef3 JoinLobbyModal for public and private lobbies (#3097)
## Description:

Replaced the src/client/JoinPrivateLobbyModal.ts with a new
src/client/JoinLobbyModal.ts which handles both public + private
lobbies.

<img width="771" height="714" alt="image"
src="https://github.com/user-attachments/assets/7ac55d91-3f0c-4f99-b960-cea9e617538d"
/>

also made a "connecting" to the lobby 
<img width="772" height="708" alt="image"
src="https://github.com/user-attachments/assets/a2812462-c5f4-459a-b63a-49d93bb2a6a2"
/>


It also needed to be updated to address the issue with the modal using
both polling + websockets

## 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
2026-02-02 21:02:20 -08:00
evanpelle 986d509a1c Merge branch 'v29' 2026-01-31 14:43:00 -08:00
evanpelle 19d0376532 better error & logging when client sends invalid intent, don't close the connection eithehr 2026-01-31 09:17:53 -08:00
Mitchell Zinck de3794313d feat: Kick player in game (#2969)
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves #2686 

## Description:
- Implemented feature for lobby creator to kick players in game.
- Added new moderation option for lobby creator, with a kick player
option if they aren't the creator, a bot, and exist in game.
- Includes a confirm kick option, and keeps track of kicked players so
that the kick option changes to "Already Kicked" if the kicked player
panel is opened again on the kicked player.

Screenshot order:
1) Open player panel
2) Click on moderation
3) Click on kick player and confirm kick
4) Player is kicked, open same player panel again and observe change in
kick status
5) Receiving player kick message

<img width="1470" height="776" alt="Screenshot 2026-01-20 at 12 33
55 PM"
src="https://github.com/user-attachments/assets/7c47b5a2-a0f8-4e92-833c-7b9732f751a8"
/>
<img width="1470" height="776" alt="Screenshot 2026-01-20 at 11 58
58 AM"
src="https://github.com/user-attachments/assets/3aa026af-9a42-4512-91b8-916f146849a6"
/>
<img width="1470" height="776" alt="Screenshot 2026-01-20 at 12 31
46 PM"
src="https://github.com/user-attachments/assets/5e1d271b-bf32-4335-8eb1-bcdf84aba8ce"
/>
<img width="1470" height="776" alt="Screenshot 2026-01-20 at 11 57
58 AM"
src="https://github.com/user-attachments/assets/7cbd5ea6-bcb6-4a35-a003-ea0add936925"
/>
<img width="1470" height="776" alt="Screenshot 2026-01-20 at 11 57
39 AM"
src="https://github.com/user-attachments/assets/4309b3e3-2fe6-48dd-8e0c-55036e567461"
/>



## 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:

mitchfz
2026-01-24 20:55:58 -08:00
Himansu Rawal 8aa3e26e70 feat: Prevent GameServer from restarting after ending by introducin… (#2923)
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves #(issue number)
#2919 


In GameManager.tick(), when a game becomes active but hasn't started, a
setTimeout for game.start() is scheduled with a 2-second delay. If the
game finishes or is cancelled within those 2 seconds, game.end() is
called, which clears the existing interval. However:

1.The 2-second timeout still fires. game.start() executes.
2. A NEW setInterval is created for turn execution.
3.Since the game is already ending/finished, it's removed from
GameManager.games, but the interval continues to run forever in the
background


## 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:

codimo
2026-01-20 19:54:57 -08:00
FloPinguin 4e8454f3cc Lobby Gold Options (Starting Gold, Gold Multiplier) 💰 (#2915)
## Description:

We might want to add this to v29 to have a third possible public game
modifier from the beginning on 😄 Would be fun

- Add starting gold option (0 to 1_000_000_000 allowed, also applies to
nations)
- Add gold multiplier option (0.1 to 1000 allowed, also applies to
nations and bots)
- Add third public game modifier (3% chance of starting with 5M gold)
- Why 5M? It's enough gold to massively change the game start but not
enough to insta-hydro someone (launcher + hydro is 6M)

<img width="357" height="140" alt="image"
src="https://github.com/user-attachments/assets/72acc15c-e788-4e04-8590-ac72dd9657c7"
/>


## 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
2026-01-16 10:20:35 -08:00
FloPinguin fb910cbff5 Lobby Gold Options (Starting Gold, Gold Multiplier) 💰 (#2915)
## Description:

We might want to add this to v29 to have a third possible public game
modifier from the beginning on 😄 Would be fun

- Add starting gold option (0 to 1_000_000_000 allowed, also applies to
nations)
- Add gold multiplier option (0.1 to 1000 allowed, also applies to
nations and bots)
- Add third public game modifier (3% chance of starting with 5M gold)
- Why 5M? It's enough gold to massively change the game start but not
enough to insta-hydro someone (launcher + hydro is 6M)

<img width="357" height="140" alt="image"
src="https://github.com/user-attachments/assets/72acc15c-e788-4e04-8590-ac72dd9657c7"
/>


## 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
2026-01-16 10:19:41 -08:00
DevelopingTom af0b8a8d50 Configurable immunity timer (#2763)
## 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>
2026-01-03 20:04:48 -08:00
Vahant Sharma b990fe6ae5 Use WebSocket intent for lobby game configuration updates (#2761)
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
2026-01-01 16:46:53 -08:00
evanpelle f5f89e4aa7 Merge branch 'v28' 2026-01-01 14:25:45 -08:00
Evan 5dc38d25f0 Add desync tracking metrics (#2707)
## Description:

- Added observable gauge to track total number of client desyncs
detected across game servers
- Tracks desyncs at the individual client level (counts each out-of-sync
client)
- Exposes metric as `openfront.desyncs.gauge` for monitoring

## 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

## Please put your Discord username so you can be contacted if a bug or
regression is found:

evan
2025-12-26 16:10:43 -08:00
YoussfeCantCode 1c52d20e83 Added pause functionality for private multiplayer games (#2657)
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"
/>
2025-12-26 10:21:18 -08:00
FloPinguin 4d5bb7a835 Cleanup nations (Part 1) 🧹 (#2637)
## 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
2025-12-18 16:20:23 -08:00
evanpelle 1d7685a5bf Merge branch 'v27' 2025-12-09 15:39:49 -08:00
Evan 075c232d8a improve game websockt (re)connection (#2584)
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
2025-12-08 14:07:07 -08:00
Lavodan fe8c0f961a Fix clients able to join above max players (#2547)
Resolves #698

## Description:


This PR fixes clients being able to join games which are already full.
This caused several bugs and glitches, like incorrect team sizes in team
games, and being stuck in spectator mode.

This fix checks for number of active clients in GameServer, and if it
sees that the lobby is full, it does not put the client in the game, and
sends an error with error key being "full-lobby"

ClientGameRunner then checks to see this error, and overrides the
default implementation of showing a popup. Instead it will leave the
lobby for the user by dispatching a leave-lobby event into the document,
and the user can then reqeue into a new game.

Here is a video showcasing how full games are handled.


https://github.com/user-attachments/assets/dc6220ea-590f-4bd1-8ca5-38c0d24ae792

## Note on testing

I wasn't able to figure out how to properly overwrite lobbyMaxPlayers
from the default config using the devconfig, so the video shows just a
hardcoded version of defaultconfig, therefore testing solo is probably
not really possible.

I just changed the function in defaultconfig for my testing to this:
```ts
  lobbyMaxPlayers(
    map: GameMapType,
    mode: GameMode,
    numPlayerTeams: TeamCountConfig | undefined,
  ): number {
	  return 1; 
	}
```

## Notes

This PR does not necessarily resolve all cases which cause 698, as for
example joining too late while there is still space is not changed at
all. For most public games, this shouldn't be an issue as the timer is
long enough for a majority to be filled up before the timer hits 0.

Additionally, spectating ongoing games should work fine, but as local
server spectating is buggy in general, I was not able to test and
confirm this 100%.

## 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
2025-12-01 15:33:24 -08:00
evanpelle 92372b770b Merge branch 'v26' into v27 2025-11-21 14:08:43 -08:00
evanpelle 7d7ef40ae4 Merge branch 'v26' 2025-11-21 14:08:27 -08:00
evanpelle 44920f030b Bugfix: Kick player in private lobby not working
Nginx was stripping query params when routing requests to workers, so the creatorClientID param was stripped when creating a private game. This caused the game server to not know who the lobby owner was, so it rejected the kick requests.
2025-11-21 14:06:31 -08:00
Ryan 34251c0673 lobby fill time added to stats (#2382)
## 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>
2025-11-10 19:53:25 -08:00
Mykola 25ea11114e Random spawn (#2375)
## Description:

https://github.com/openfrontio/OpenFrontIO/issues/2352

This is my first PR in this project, and I’ll continue refining it based
on feedback.

<img width="1088" height="859" alt="image"
src="https://github.com/user-attachments/assets/07f4f8b1-52fa-4136-add4-19b00aefd963"
/>

<img width="1157" height="783" alt="image"
src="https://github.com/user-attachments/assets/1c5be80d-72f8-4ead-8d4b-706a3a04fd73"
/>

<img width="1488" height="777" alt="image"
src="https://github.com/user-attachments/assets/4d743548-f0c3-4579-963b-43676f68fab1"
/>

<img width="1499" height="778" alt="image"
src="https://github.com/user-attachments/assets/f808e44f-ef97-467f-9e41-812e2857c36e"
/>


## 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:

nikolaj_mykola

---------

Co-authored-by: Evan <evanpelle@gmail.com>
2025-11-06 15:49:37 -08:00
evanpelle 02bdaaa2c6 Merge branch 'v26' 2025-10-28 14:10:27 -07:00
Evan 3fd38e7306 Add clanTag to GameRecord for archiving (#2314)
## 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
2025-10-27 20:05:09 -07:00