Commit Graph

3289 Commits

Author SHA1 Message Date
Ryan Barlow cd17df29fe rabbit 2026-03-01 14:26:21 +00:00
Ryan Barlow 15bcc0241e update 2026-03-01 13:16:22 +00:00
Ryan Barlow 4c560c7ba9 rules update 2026-02-28 22:43:13 +00:00
Ryan Barlow 42c1d3ed7a compet 2026-02-28 22:43:13 +00: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
TsProphet94 5542ac12e1 Add new map: Bosphorus Straits (#2927)
## Description:

Adds a new Bosphorus Map (Turkey). One of the key strategic locations in
the world and control access to the Black Sea. Smaller map that most to
facilitate smaller FFA and Team games. Added are a selection of nations
that correspond to the location.

<img width="1000" height="612" alt="image"
src="https://github.com/user-attachments/assets/27a6debc-a33b-4b54-b522-69ab814c39f0"
/>

![Image 16-01-2026 at 17
39](https://github.com/user-attachments/assets/9660de13-53b3-4a94-852f-95ba16e4bb73)
![Image 16-01-2026 at 17 39
(1)](https://github.com/user-attachments/assets/d3372919-da4e-4507-a3b8-4bfbdde1ccd4)


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

TSProphet

---------

Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
Co-authored-by: iamlewis <lewismmmm@gmail.com>
2026-02-28 14:19:59 -08:00
TsProphet94 11d3228608 Adds Bering Strait map. (#2924)
## Description:

PR to add Bering Strait map. Produced using TOPO data from real world
location, rivers and lakes correct to real world location. The map is
slightly smaller than most so facilitates smaller FFA games or 2 team
game modes.

The centre island has been increased in size to enable better warfare to
capture the strategic location.

<img width="1500" height="918" alt="image"
src="https://github.com/user-attachments/assets/bc9b2e69-cef1-4f21-92b5-4ffdce5812e1"
/>

![Image 16-01-2026 at 15
37](https://github.com/user-attachments/assets/f9377af5-d82a-41e0-9682-d8435fce686b)

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

TSProphet

---------

Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-02-28 22:12:03 +00:00
scamiv 9fc11b7b9a perf(worker): remove heartbeat; batch game updates (#3308)
## Description:
Removes the client-driven heartbeat loop and switches worker tick
execution to a worker-owned drain scheduler with batched game update
delivery.

## Why
The previous flow required the client to send a `heartbeat` every
animation frame just to keep the worker progressing turns. That had two
costs:

1. Simulation progress was coupled to browser frame cadence.
2. Catch-up periods produced many single `game_update` messages,
increasing message overhead and main-thread wakeups.

## What Changed

### 1) Remove heartbeat protocol
- Deleted `heartbeat` from `WorkerMessageType`.
- Removed `HeartbeatMessage` from `MainThreadMessage`.
- Removed `sendHeartbeat()` from `WorkerClient`.
- Removed the `requestAnimationFrame` keep-alive loop in
`ClientGameRunner`.

Files:
- `src/client/ClientGameRunner.ts`
- `src/core/worker/WorkerClient.ts`
- `src/core/worker/WorkerMessages.ts`
- `src/core/worker/Worker.worker.ts`

### 2) Add batched worker-to-client updates
- Added `game_update_batch` message type and `GameUpdateBatchMessage`.
- Worker now emits one batch message containing multiple tick updates.
- `WorkerClient` handles `game_update_batch` by replaying updates to the
existing callback in order.

Files:
- `src/core/worker/WorkerMessages.ts`
- `src/core/worker/WorkerClient.ts`

### 3) Move tick draining into worker
- Added a scheduler (`scheduleDrain`) and drain loop (`drain`) in
`Worker.worker.ts`.
- On each `turn` message, worker enqueues turn and schedules drain.
- Drain executes up to `MAX_TICKS_BEFORE_YIELD = 4` ticks per cycle,
then yields with `setTimeout(..., 0)`.
- Tick updates are collected into a batch and sent once with
transferables:
  - `packedTileUpdates.buffer`
  - `packedMotionPlans.buffer` (when present)
- If backlog remains, drain reschedules itself.

File:
- `src/core/worker/Worker.worker.ts`

## Behavioral Notes
- No server protocol changes.
- Ggame update callback contract remains the same (still receives one
`GameUpdateViewData` at a time in order).
- Ordering is preserved: `WorkerClient` iterates batch entries in
sequence.
- Error updates are still filtered from update delivery in the worker
batch path (same effective behavior as before for normal update flow).

## Expected Impact
- Fewer `postMessage` calls during backlog and burst turn delivery.
- Lower message overhead and fewer main-thread interrupts.
- Less dependence on UI frame timing for worker progress.
- Better catch-up stability due to explicit periodic yielding.

## Risk Areas
- Drain scheduling edge cases (re-entrancy / lost wake-ups).
- Mitigated with `drainScheduled`, `draining`, and `drainRequested`
flags.
- Larger per-message payloads due to batching.
  - Bounded by `MAX_TICKS_BEFORE_YIELD`.
- Any assumptions in downstream code about receiving only `game_update`.
  - Handled by adding `game_update_batch` support in `WorkerClient`.

## 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
2026-02-28 21:58:32 +00:00
scamiv c911bfb2d8 Packed unit updates / MotionPlans (#3292)
## Description:

Reduce per-step `Unit` update traffic by shipping packed motion plans
and letting the client advance plan-driven units locally.

Changes:
- Add packed motion plan records (`packedMotionPlans?: Uint32Array`) to
game updates and transfer the buffer worker -> main.
- Introduce `src/core/game/MotionPlans.ts` (schema + pack/unpack) for
grid + train motion plans.
- Extend `Game` with `recordMotionPlan(...)` and
`drainPackedMotionPlans()`, and implement buffering/packing in
`GameImpl`.
- Treat units with motion plans as “plan-driven”: suppress per-tile
`Unit` updates on `move()` and advance positions client-side.
- Emit motion plans from executions:
- `TradeShipExecution`: record/update grid motion plans and `touch()`
when changing target after capture.
- `TransportShipExecution`: record initial plan and update it when
destination changes.
  - `TrainExecution`: record a train plan on init (engine + cars).
- Client: apply motion plans in `GameView` and ensure `UnitLayer`
updates sprites for motion-planned units even when no `Unit` updates
arrived.

## Please complete the following:

- [ ] I have added screenshots for all UI updates
- [ ] 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
- [ ] 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
2026-02-27 20:54:42 -08:00
scamiv 1cafc6bc25 perf(translateText): speed up translateText (#3296)
## Description:

Cache lang-selector lookup
Avoid per-call empty params allocation
Add fast-path for non-ICU strings

## Please complete the following:

- [ ] I have added screenshots for all UI updates
- [ ] 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
- [ ] 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
2026-02-27 18:20:04 -08:00
evanpelle f09177f8fe Merge branch 'v29' 2026-02-26 17:40:57 -06:00
Evan 1f05e22277 Add Traefik integration to deployment script (#3302)
## Description:

Connects deployed containers to Traefik for automatic reverse proxy
routing, replacing the previous Cloudflare Tunnel approach.

```
 docker inspect openfront-staging-traefik --format '{{json .Config.Labels}}' | jq
{
  "traefik.enable": "true",
  "traefik.http.routers.openfront-staging-traefik.entrypoints": "web",
  "traefik.http.routers.openfront-staging-traefik.rule": "Host(`traefik.openfront.dev`)",
  "traefik.http.services.openfront-staging-traefik.loadbalancer.server.port": "80"
}
```

## 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
v0.29.18
2026-02-26 17:04:53 -06:00
Aotumuri a7b137b3b7 fix: place select controls below text (#3299)
## Description:

Updated `setting-select` layout to a vertical flow:
  - Header
  - Description
  - Selector

before
<img width="1306" height="770" alt="スクリーンショット 2026-02-26 19 10 36"
src="https://github.com/user-attachments/assets/7da2a9af-b8bd-4f7f-8cd6-f22946d07720"
/>
<img width="372" height="749" alt="スクリーンショット 2026-02-26 19 14 18"
src="https://github.com/user-attachments/assets/50148101-4c9e-4db5-b6c3-53f819ee9e6a"
/>

after
<img width="1470" height="827" alt="スクリーンショット 2026-02-26 19 10 01"
src="https://github.com/user-attachments/assets/9e36420b-a616-4056-8b11-ebb4bf25a5b2"
/>
<img width="692" height="832" alt="スクリーンショット 2026-02-26 19 10 15"
src="https://github.com/user-attachments/assets/3b3e8fbf-fd57-47c1-9c87-763df81d673a"
/>


## 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:
aotumuri
2026-02-26 10:24:22 +00:00
Mattia Migliorini 7b785ea79a Fix alliance renewal prompt incorrectly dismissed for both players (#3297)
## Description:

NOTE: Applies to current main / beta version. Needs to be included in
v30.

When a player clicked "Renew Alliance", the `AllianceExtensionUpdate`
broadcast caused both players' renewal prompts to be removed, even the
one who hadn't yet acted. This happened because
`onAllianceExtensionEvent` called `removeAllianceRenewalEvents`
unconditionally on every client.

This PR fixes the behavior by calling `removeAllianceRenewalEvents` only
for the player that executed the action.

## 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
2026-02-25 21:12:58 -06:00
Aotumuri bd3db55a22 Add configurable attack ratio keybind increment setting (#2835)
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves #2822

## Description:

Adds an attack ratio keybind increment setting with a new dropdown UI,
wires keybinds to use the configured step, updates the attack ratio
adjustment logic, and makes the select reflect stored settings.

<img width="806" height="165" alt="スクリーンショット 2026-01-12 9 11 12"
src="https://github.com/user-attachments/assets/c6eaa96d-e147-4927-b3ed-964e832ecc36"
/>

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

---------

Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
Co-authored-by: iamlewis <lewismmmm@gmail.com>
2026-02-25 23:31:36 +00:00
bijx 7855e1b0e9 Feat: Troop transport retreats to closest owned tile v2 (#3286)
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves #1139

## Description:

New version of the #2789 PR that is cleaner after changes made to old
pathfinding logic.

Adds logic to troop transport retreat behaviour which retreats a
transport to the closest owned tile instead of the source. Now if no
shores are detected (you lost all your shoreline while the transport was
out) we handle the return case same as if the original source was no
longer your territory.

<img width="2541" height="1593" alt="image"
src="https://github.com/user-attachments/assets/4d2ff5e7-d10d-40f4-80e0-9f029cff61a2"
/>

## Video example from previous PR (works the exact same way in this PR):


https://github.com/user-attachments/assets/e43a3b10-e8b0-4f23-87f3-2dc4739de880

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

bijx
2026-02-24 21:31:06 -06:00
dependabot[bot] e39140733b Bump minimatch from 3.1.2 to 3.1.3 in the npm_and_yarn group across 1 directory (#3294)
Bumps the npm_and_yarn group with 1 update in the / directory:
[minimatch](https://github.com/isaacs/minimatch).

Updates `minimatch` from 3.1.2 to 3.1.3
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/isaacs/minimatch/commit/00c323b188b704e5d4bc534ecec2268cfa70a32a"><code>00c323b</code></a>
3.1.3</li>
<li><a
href="https://github.com/isaacs/minimatch/commit/30486b2048929264f44d18822891cfffa02af78b"><code>30486b2</code></a>
update CI matrix and actions</li>
<li><a
href="https://github.com/isaacs/minimatch/commit/9c31b2d4e0af72a6c2d2d62c5dbc2247da669802"><code>9c31b2d</code></a>
update test expectations for coalesced consecutive stars</li>
<li><a
href="https://github.com/isaacs/minimatch/commit/46fe687857cf02f6cf45469cc593b97e11b10c96"><code>46fe687</code></a>
coalesce consecutive non-globstar * characters</li>
<li><a
href="https://github.com/isaacs/minimatch/commit/5a9ccbda64befc5d94b965534dbea2853c92aebd"><code>5a9ccbd</code></a>
[meta] update publishConfig.tag to legacy-v3</li>
<li>See full diff in <a
href="https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=minimatch&package-manager=npm_and_yarn&previous-version=3.1.2&new-version=3.1.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/openfrontio/OpenFrontIO/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-24 21:28:26 -06:00
evanpelle 9b96b07820 test: add vitest-canvas-mock for local canvas support
Fixes UILayer tests failing locally due to the native canvas package
not being compiled. vitest-canvas-mock provides a jsdom-compatible
Canvas 2D API mock without requiring native build tools.
2026-02-24 15:59:14 -06:00
Evan 7f03072e9b revert skin trials (#3293)
## Description:

Skin trials has been a failure, very low fill rate and cause a major
drop in sales.

reverts 


https://github.com/openfrontio/OpenFrontIO/commit/97d0a05d58e926e3de4ba46d8dd14a04d60d6698

## 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
v0.29.17
2026-02-24 15:47:21 -06:00
FloPinguin 339ace0bd6 v30 nuke wars preparation: Disable boats & Team spawn zones (#3263)
## Description:

Preparation for nuke wars, for v30.
Next PR will be adding the nuke wars modifier for public games, but
Wonders https://github.com/openfrontio/OpenFrontIO/pull/3224 needs to be
merged first to avoid merge conflicts.

### 1. Disable boats setting

It's possible to disable `UnitType.TransportShip` now. Because they are
not needed in nuke wars and can even be annoying.

<img width="720" height="320" alt="image"
src="https://github.com/user-attachments/assets/661bc10d-b204-4b4f-b876-ee7c9b92de8c"
/>

### 2. Team spawn zones for random spawn

Maps can have `teamGameSpawnAreas` in their json file now.
Spawn areas are currently active if 
- a supported map is chosen (Baikal Nuke Wars or Four Islands)
- a supported team size is chosen (2 teams on Baikal Nuke Wars or 2/4
teams on Four Islands)
- random spawn is enabled

## 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
v0.30.0-beta1
2026-02-23 16:12:24 -06:00
scamiv 4b917c4153 Performance Overlay rework/redesign (#3274)
## Description:
updates the Performance Overlay to be more usable
(draggable/resizable/scrollable), adds tick-level metrics (TPS +
per-layer tick timings), and reduces overhead when the overlay is
hidden.

### UI/UX
- Overlay layout updated to a fixed, pixel-positioned panel (default
near top-left) with a dedicated drag handle.
- Overlay is touch-draggable (pointer events) and remains usable on
small viewports via internal scrolling.
- Overlay width is resizable with a right-edge handle; width is clamped
to viewport bounds.
- Render/tick layer breakdown sections are collapsible, with headers and
“last tick” summaries.

### New metrics
- Adds TPS reporting:
  - Current TPS (ticks in the last 1s).
- Average TPS over the last ~60s, computed using elapsed time so it’s
accurate before a full 60s passes.
- Adds per-layer tick profiling (“Tick Layers”) alongside render
profiling (“Render Layers”).
- Adds “render-per-tick” metrics so render-layer costs can be understood
per simulation tick (frames + per-layer totals).

### Performance / overhead
- Avoids profiling overhead when the overlay is hidden:
- `GameRenderer` only calls `FrameProfiler.clear()/consume()` and
per-layer `start/end` when profiling is enabled.
- Tick-layer duration tracking is only collected when profiling is
enabled.

### Settings plumbing
- `UserSettings` now dispatches a `user-settings-changed` `CustomEvent`
on `set()` / `setFloat()`.
- The overlay listens for `settings.performanceOverlay` changes so
visibility stays in sync even when toggled outside the overlay.

## Implementation notes (by file)

- `src/client/graphics/layers/PerformanceOverlay.ts`
  - Adds TPS tracking using a timestamp ring + moving heads (1s / 60s).
- Adds UI state for collapsibles, drag + resize pointer tracking, and
new breakdown models:
    - Render layers: EMA avg/max + per-tick render aggregation.
    - Tick layers: EMA avg/max + last-tick durations.
- Copy-to-clipboard snapshot now includes TPS, tick layers, and
render-per-tick last-tick details.

- `src/client/graphics/GameRenderer.ts`
  - Gates render-layer profiling behind `FrameProfiler.isEnabled()`.
- Accumulates per-render-layer timings across frames and publishes them
once per tick via `updateRenderPerTickMetrics(...)`.
- Measures tick-layer durations (per layer `tick()` call) and publishes
them via `updateTickLayerMetrics(...)`.

- `src/core/game/UserSettings.ts`
- Adds `emitChange(key, value)` to dispatch `user-settings-changed` to
`globalThis` (best-effort).

- `resources/lang/en.json`
- Adds/updates `performance_overlay.*` strings for TPS and the new
render/tick layer sections.

## Please complete the following:

- [ ] I have added screenshots for all UI updates
- [ ] 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
- [ ] 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

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-02-23 14:22:56 -06:00
Nolhan e5ce278cb1 refactor: enhanced Join Private Lobby form (#3284)
## Description:
This pull request enhances the `JoinLobbyModal` component by using the
`<form>` component and the `@submit` event. It allows the user to use
the enter (return) key to submit instead of grabbing its mouse to click
on "Join Lobby".
It also introduces a new `submit` argument to the `Button` component.

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

PS: The tests from `tests/InputHandler.test.ts` are failing on both
`main` and my branch. EDIT: They no longer fail through the workflow so
I guess I didn't have the correct environment
2026-02-23 19:02:24 +00:00
VariableVince 4788316504 Small refactor: unnecessary Array.from (#3279)
## Description:

Array.from was performed on this.player.alliances(), which already
returns an array. Also it was saved in a const which isn't strictly
necessary, same goes for the array in the loop below 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

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

tryout33
2026-02-23 09:17:15 +00:00
VariableVince c3a8d06cbb Perf: tradeship spawn (#3240)
## Description:

Please merge for v30 if possible.

Use .find instead of .filter for tradeShipSpawn since we're only looking
for the first (if any) port found at the given tile anyway.

Also just return targetTile instead of getting porr.tile() because
targetTile is tile we found the port on.

Also use no intermediate const, just return right away based on outcome
of units.find.

Found when working on PR #3220. But tradeShipSpawn is out of 3220's
scope since it won't be called by playerImpl buildableUnits() anymore,
it should and will be only ever used by TradeShipExecution via
playerImpl canBuild().

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

tryout33
2026-02-22 21:57:12 -06:00
FloPinguin d48415ac2a Game creation rate: 1 minute -> 2 minutes 🔧 (#3259)
## Description:

Can we maintain a one-minute game creation rate with three times as many
lobbies?

I don’t think so - we should allow more time for players to join.

It still shouldn’t bore players, since they can also join the special
mix lobby. That lobby includes both FFA and team games and fills more
quickly due to the higher frequency of compact-map matches (the Wonder
PR https://github.com/openfrontio/OpenFrontIO/pull/3224 needs to be
merged for that).

## 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-02-22 21:56:09 -06:00
VariableVince 1d73401c72 Small perf: find() instead of filter() for retreat (#3277)
## Description:

Only need find() instead of filter() in orderRetreat and executeRetreat,
since we just need the first hit. Small perf win.

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

tryout33
2026-02-22 21:54:10 -06:00
FloPinguin b1c4c9723c Followup leaderboard fix... (for mobile) (#3281)
## Description:

Column widths were off for some reason, I thought they were fixed...
So here is a followup PR

Previous:

<img width="467" height="782" alt="image"
src="https://github.com/user-attachments/assets/f5a084ea-e8b9-473b-abe4-d8c9d0d5d9de"
/>

Now:

<img width="454" height="779" alt="image"
src="https://github.com/user-attachments/assets/d845ec32-e76e-4ad5-aa62-5642a4c78da4"
/>

## 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-02-22 21:52:50 -06:00
Mykola f7b39faca7 Spawn. Fix respawn near the previos spawn (#3278)
## Description:

Because spawning is prohibited on tiles that have an owner, this created
a problem when a person tried to spawn near the center of their previous
spawn. This was resolved by relinquishing all the tiles conquered by the
previous spawn.

## 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
2026-02-22 22:10:00 +00:00
FloPinguin edc3e20a9f Improve 1vs1 ranked leaderboard (#3270)
## Description:

The two tables look much more similar now
And you can see the player names now

Before:


https://github.com/user-attachments/assets/59f94e1a-5909-4d13-8ff3-bd36775f4ae6

After:


https://github.com/user-attachments/assets/51234d14-20c2-4b14-a7cc-ceef7cf9a8fd

## 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-02-22 18:52:59 +00:00
FloPinguin 9af4ff806c Fix nation name typo 🔧 (#3269)
## Description:

Fix nation name typo 🔧

## 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: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-02-22 16:53:12 +00:00
FloPinguin d50768e719 Put attacks display above events on mobile 🖌️ (#3272)
## Description:

tryout wanted that :)


https://github.com/user-attachments/assets/eeaf5447-cffe-4c4f-9734-ebc3edd9255e

## 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: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-02-22 16:52:34 +00:00
Mykola 097c42740c Random spawn. Avoid spawning near water. (#3009)
## Description:

Fixing
https://discord.com/channels/1359946986937258015/1360078040222142564/1463898386854973642

Now, if not all tiles on the spawn circle can be owned, the algorithm
tries to select another random spawn tile.

## 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: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-02-22 15:51:05 +00:00
FloPinguin 7c6c2b1fd8 Adjust bottom margin for lobby card layout on homepage (#3268)
## Description:

Before:

<img width="744" height="540" alt="image"
src="https://github.com/user-attachments/assets/79baafa3-0c80-470d-a7bc-da428a0d4402"
/>

After:

<img width="746" height="509" alt="image"
src="https://github.com/user-attachments/assets/ca3c57d4-0854-4879-9792-ee8e00ae164e"
/>

## 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: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-02-22 14:35:05 +00:00
FloPinguin 2fd8757e66 Notification dot for new versions (+ mobile dot improvements) (#3265)
## Description:

- **News notification dot (desktop + mobile)**: Added a red pinging dot
on the "News" nav entry that appears when a new version is released. The
current app version is saved to localStorage (`newsSeenVersion`) on
first visit. On subsequent visits, if the version has changed, the dot
appears. Clicking "News" dismisses it by updating the stored version.

- **Mobile Store**: Replaced the static "NEW" text badge on the Store
nav item with a red pinging dot (matching the desktop navbar style). The
dot is conditionally shown based on cosmetics hash changes tracked in
localStorage, and dismissed when the user clicks Store.

- **Help dot on mobile**: Added the yellow help dot (already present on
desktop) to the mobile navbar for consistency, shown for users with
fewer than 10 games played.

### Screenshots:

<img width="1028" height="97" alt="Screenshot 2026-02-21 174029"
src="https://github.com/user-attachments/assets/1ed460dd-4e41-4287-bcb9-73f431e8a953"
/>

<img width="513" height="700" alt="Screenshot 2026-02-21 174333"
src="https://github.com/user-attachments/assets/c6b81296-d36b-424e-9637-e738acd8007a"
/>

## 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-02-21 21:15:36 -06:00
Mattia Migliorini 6a30d2b38b Smarter factory placement for Nation AI 🤖 (#3244)
## Description:

Introduces a dedicated `factoryValue()` scoring function for AI factory
placement, replacing the generic `interiorStructureValue()` previously
shared with cities and missile silos.

Scoring criteria:
- High elevation and spacing from other factories (unchanged from
city/silo logic)
- Rail connectivity: bonus per distinct rail cluster reachable within
`trainStationMaxRange`, weighted by trade gold potential — allied
clusters score highest (1.0), team/neutral clusters score ~0.71, own
clusters ~0.29 (based on `config.tradeGold()` values). Based on
difficulty
- Cluster deduplication: connecting to the same cluster multiple times
does not inflate the score
- Embargoed and bot neighbors are excluded; all other non-embargoed
neighbors are included

The result is that the AI tends to place factories where they can bridge
separate rail networks or connect to high-value trade partners, rather
than deep in its own interior.

### EDIT

Added a dedicated `cityValue()` scoring function that takes into account
the connectivity score. This allows placement of cities in a
"factory-aware" way, while also enforcing spreading structures (we want
the network to grow, not a cluster of cities and factories all
together).

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

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-02-21 21:12:44 -06: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
dependabot[bot] 6ed203529b Bump fast-xml-parser from 5.3.4 to 5.3.6 in the npm_and_yarn group across 1 directory (#3266)
Bumps the npm_and_yarn group with 1 update in the / directory:
[fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser).

Updates `fast-xml-parser` from 5.3.4 to 5.3.6
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/releases">fast-xml-parser's
releases</a>.</em></p>
<blockquote>
<h2>Entity security and performance</h2>
<ul>
<li>Improve security and performance of entity processing
<ul>
<li>new options <code>maxEntitySize</code>,
<code>maxExpansionDepth</code>, <code>maxTotalExpansions</code>,
<code>maxExpandedLength</code>,
<code>allowedTags</code>,<code>tagFilter</code></li>
<li>fast return when no edtity is present</li>
<li>improvement replacement logic to reduce number of calls</li>
<li></li>
</ul>
</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.5...v5.3.6">https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.5...v5.3.6</a></p>
<h2>v5.3.5</h2>
<h2>What's Changed</h2>
<ul>
<li>Add missing exports to fxp commonjs types by <a
href="https://github.com/jeremymeng"><code>@​jeremymeng</code></a> in <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/pull/782">NaturalIntelligence/fast-xml-parser#782</a></li>
<li>fix: Escape regex char in entity name</li>
<li>update strnum to 2.1.2</li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/jeremymeng"><code>@​jeremymeng</code></a> made
their first contribution in <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/pull/782">NaturalIntelligence/fast-xml-parser#782</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.4...v5.3.5">https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.4...v5.3.5</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md">fast-xml-parser's
changelog</a>.</em></p>
<blockquote>
<p><!-- raw HTML omitted -->Note: If you find missing information about
particular minor version, that version must have been changed without
any functional change in this library.<!-- raw HTML omitted --></p>
<p>5.3.7
<strong>5.3.7 / 2026-02-20</strong></p>
<ul>
<li>fix typings for CJS (By <a
href="https://github.com/Drarig29">Corentin Girard</a>)</li>
</ul>
<p><strong>5.3.6 / 2026-02-14</strong></p>
<ul>
<li>Improve security and performance of entity processing
<ul>
<li>new options <code>maxEntitySize</code>,
<code>maxExpansionDepth</code>, <code>maxTotalExpansions</code>,
<code>maxExpandedLength</code>,
<code>allowedTags</code>,<code>tagFilter</code></li>
<li>fast return when no edtity is present</li>
<li>improvement replacement logic to reduce number of calls</li>
</ul>
</li>
</ul>
<p><strong>5.3.5 / 2026-02-08</strong></p>
<ul>
<li>fix: Escape regex char in entity name</li>
<li>update strnum to 2.1.2</li>
<li>add missing exports in CJS typings</li>
</ul>
<p><strong>5.3.4 / 2026-01-30</strong></p>
<ul>
<li>fix: handle HTML numeric and hex entities when out of range</li>
</ul>
<p><strong>5.3.3 / 2025-12-12</strong></p>
<ul>
<li>fix <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/775">#775</a>:
transformTagName with allowBooleanAttributes adds an unnecessary
attribute</li>
</ul>
<p><strong>5.3.2 / 2025-11-14</strong></p>
<ul>
<li>fix for import statement for v6</li>
</ul>
<p><strong>5.3.1 / 2025-11-03</strong></p>
<ul>
<li>Performance improvement for stopNodes (By <a
href="https://github.com/macieklamberski">Maciek Lamberski</a>)</li>
</ul>
<p><strong>5.3.0 / 2025-10-03</strong></p>
<ul>
<li>Use <code>Uint8Array</code> in place of <code>Buffer</code> in
Parser</li>
</ul>
<p><strong>5.2.5 / 2025-06-08</strong></p>
<ul>
<li>Inform user to use <a
href="https://github.com/NaturalIntelligence/fxp-cli">fxp-cli</a>
instead of in-built CLI feature</li>
<li>Export typings  for direct use</li>
</ul>
<p><strong>5.2.4 / 2025-06-06</strong></p>
<ul>
<li>fix (<a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/747">#747</a>):
fix EMPTY and ANY with ELEMENT in DOCTYPE</li>
</ul>
<p><strong>5.2.3 / 2025-05-11</strong></p>
<ul>
<li>fix (<a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/747">#747</a>):
support EMPTY and ANY with ELEMENT in DOCTYPE</li>
</ul>
<p><strong>5.2.2 / 2025-05-05</strong></p>
<ul>
<li>fix (<a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/746">#746</a>):
update strnum to fix parsing issues related to enotations</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/ecb2ca118ad3d6c62f2cc90416b58da24db5d18b"><code>ecb2ca1</code></a>
update release info</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/910dae5be2de2955e968558fadf6e8f74f117a77"><code>910dae5</code></a>
fix entities performance &amp; security issues</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/fe9a85270122036ae22637167ce38a5f71b73a5f"><code>fe9a852</code></a>
update strnum and release detail</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/943ef0eb1b2d3284e72dd74f44a042ee9f07026e"><code>943ef0e</code></a>
fix: Escape regex char in entity name</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/ddcd0acf26ddd682cb0dc15a2bd6aa3b96bb1e69"><code>ddcd0ac</code></a>
Escape regex char in entity name</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/341b582219b1eb57e4c34ca58881602cba6b8711"><code>341b582</code></a>
Add missing exports to fxp commonjs types (<a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/782">#782</a>)</li>
<li>See full diff in <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.4...v5.3.6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=fast-xml-parser&package-manager=npm_and_yarn&previous-version=5.3.4&new-version=5.3.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/openfrontio/OpenFrontIO/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-21 21:06:07 -06:00
Josh Harris 05af154b58 feat(server): add health api endpoint for increased observability (#3264)
## Description:

Adds an additional API endpoint to the server for health, using the
master lobby service as the health metric. The master lobby service is
considered healthy if the lobby service has started (i.e. it had enough
ready workers to start), and the current amount of ready workers is more
than half of the desired number.

This means that we won't show as healthy until all the workers start,
and then we will continue to show as healthy even if a few workers
crash, as long as at least more than half are still running. Any less
than that, and the service becomes unhealthy.

This also is set to "no cache" in the nginx config. This is to ensure
that any checks of the server health show the true value, and cannot
show false/stale data served by nginx, cloudflare, or anything else.

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

jish
2026-02-21 16:52:47 -06:00
FloPinguin f09d9a3a5f Nations can overwhelm SAMs now 💥 (+ 3 little nation improvements) (#3246)
## Description:

### SAM Overwhelming (`NationNukeBehavior.ts`)

On Impossible difficulty, nations can now destroy enemy SAMs by
overwhelming them with coordinated atom bomb salvos. When no good nuke
target is found (all trajectories intercepted by SAMs), the nations
will:

- Identify the easiest enemy SAM to destroy (lowest level first)
- Calculate the total interception capacity of all covering SAMs and
send enough bombs to overwhelm them (+1 extra per 5 needed to account
for enemy building more SAMs during flight)
- Plan launches in NukeExecution's Manhattan-distance silo order,
tracking which silos have interceptable trajectories (wasted bombs)
- Use a sliding window over parabolic flight times to find the best
cluster of bombs that can arrive within half the SAM cooldown window
- Compute per-bomb wait ticks to synchronize arrivals from silos at
different distances
- Skip launching if a salvo is already in flight
- Upgrade the best SAM-protected silo when silo capacity is
insufficient; wait and save gold when only gold is lacking


https://github.com/user-attachments/assets/14fa592f-2902-4604-8e37-1eba2b2f0b85

### 2-Player Endgame Handling (`NationNukeBehavior.ts`)

- On Hard/Impossible with only 2 players remaining,
`findBestNukeTarget()` directly targets the other player (bypasses all
priority logic)
- `getPerceivedNukeCost()` returns actual cost (no MIRV saving
inflation) when only 2 players are left

### SAM Build Rate (`NationStructureBehavior.ts`)

- Reduced SAM perceived cost increase per owned from 1.0 to 0.5, so
nations build more SAMs

### Island Attack Variety (`AiAttackBehavior.ts`)

- `findNearestIslandEnemy()` now collects up to 2 reachable candidates
and has a 33% chance to pick the second-nearest, adding variety to boat
attack targeting

## 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-02-20 23:16:03 -06:00
scamiv ea2a76609f perf(core): speed up packedTileUpdates (Uint32 pairs, no tile wrappers) (#3255)
## Description
Reduces CPU + GC pressure from tile update serialization.

**What changed**
- Switched `packedTileUpdates` from `BigUint64Array` (BigInt packing) to
`Uint32Array` `[tileRef, state]` pairs, updating `GameView` ingestion.
- Updated tile state to use `GameMap.tileState(tile)` and
`GameMap.updateTile(tile, state)`.
- Removed per-tile `GameUpdateType.Tile` wrapper allocations by
recording raw `(tile, state)` pairs in `GameImpl` and draining them via
`drainPackedTileUpdates()` in `GameRunner`.

**Why it’s faster**
- Avoids BigInt and pack/unpack.
- Avoids per-tile object allocations.

**Compatibility**
- Wire format change: `packedTileUpdates` is now `Uint32Array` pairs
instead of `BigUint64Array`.

## Please complete the following:

- [ ] I have added screenshots for all UI updates
- [ ] 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
- [ ] 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
2026-02-20 23:01:03 -06:00
VariableVince 444aa16ac8 Fix: less console warn spam on each attack-click (#3262)
## Description:

Rabbit suggestion to move console warn for not being able to send boat
to doBoatAttackUnderCursor and remove it from canBoatAttack.

On each attack-click on land, if that land is own land or ally and
canAttack is false, it will check canAutoBoat. Even if user had no
intention to attack, there would be a warning that no boat could be
send. Now only do that warning when user actually intended to send a
boat using hotkey G which calls doBoatAttackUnderCursor.

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

tryout33
2026-02-20 22:58:14 -06:00
evanpelle b865e0af8f bugfix: ranked 1v1 did not set the start time, so the game started immediately before players had time to join 2026-02-20 13:32:36 -06:00
FloPinguin 07b5d6e41f Prevent self-retaliation crash when own nuke destroys own ship 🔧 (#3260)
## Description:

Noticed this in two singleplayer games:

When a nation's nuke destroys its own transport/trade ship in the blast
radius, `NationWarshipBehavior` incorrectly tries to retaliate against
itself, calling `updateRelation(self)` which throws (GameRunner tick
error).

Added a self-check in `maybeRetaliateWithWarship` to skip retaliation
when the destroyer is the player itself.

## 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-02-20 17:44:57 +00:00
FloPinguin 354c703d39 Reduce main page images filesize by 90% 🖼️ (#3258)
## Description:

Reduce main page images filesize by 90%
Converted to webp

## 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-02-20 17:27:16 +00:00
Mattia Migliorini 90204f6628 Add alliance renewal action to Radial Menu (#3148)
## Description:

The following PR replaces the (disabled) alliance request button with an
alliance extension/renewal button when the alliance with the target
player is expiring.

Agreeing to renewal via radial menu also hides the message in the
EventsDisplay.

<img width="369" height="364" alt="image"
src="https://github.com/user-attachments/assets/d8040f5c-ad7b-47d0-852f-925ecbf273a8"
/>


https://github.com/user-attachments/assets/aa589edf-6505-46bf-88a3-aa4c2df9137f

Icon size adjusted:

<img width="294" height="252" alt="image"
src="https://github.com/user-attachments/assets/7ca63500-b1fb-427b-965c-cf121a5213da"
/>

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-02-19 19:47:57 -06:00
scamiv f6a08e16db Perf alloc (#3241)
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves #(issue number)

## Description:

## PR Title
perf(core): reduce hot-path allocations & safe optimizations

This PR brings in a set of allocation-focused optimizations in core hot
paths

### Scope
- `src/core/execution/NukeExecution.ts`
- `src/core/execution/WarshipExecution.ts`
- `src/core/game/UnitGrid.ts`
- `src/core/game/PlayerImpl.ts`
- `src/core/configuration/DefaultConfig.ts`
- `src/core/execution/SAMLauncherExecution.ts`

### What Changed
- `NukeExecution.detonate`: reduced call overhead/allocations by caching
`mg`/`config`, avoiding repeated lookups, and using allocation-free
loops (no `forEach` closures) in the diminishing-effect pass.
- `WarshipExecution.findTargetUnit`: replaced allocate+sort flow with
single-pass best-target selection.
- `UnitGrid.nearbyUnits`: reduced call overhead and allocations via
single-type fast path and cached query coordinates.
- `PlayerImpl.units`: added fast paths for common small-arity type
queries (1-3 unit types).
- `DefaultConfig.unitInfo`: cached `UnitInfo` objects per `UnitType` to
avoid repeated object/closure creation.
- `SAMLauncherExecution` targeting: removed sort churn and streamlined
target selection with single-pass hydrogen prioritization.



### Rebase
- One conflict was resolved in `NukeExecution.detonate` by keeping
`main`'s diminishing-effect-per-impacted-tile behavior, while retaining
the allocation-reduction refactors.


## Please complete the following:

- [ ] I have added screenshots for all UI updates
- [ ] 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
- [ ] 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
2026-02-19 19:01:12 -06:00
VariableVince c235debb57 Cleanup: Replace literals by enums (#3252)
## Description:

Some literals were present that could/should have been enums. Replaced
them.

For Util.ts > createRandomName, also changed type of parameter
playerType from string to PlayerType. All callers already send it this
type.

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

tryout33
2026-02-19 16:58:07 -06:00
scamiv fcf45db672 perf(core): reduce packed tile update churn (#3253)
## Description:
- Build packedTileUpdates directly into a BigUint64Array (avoid
intermediate JS array + copy).

- Transfer packedTileUpdates.buffer in worker postMessage to avoid
structured-clone.


- Avoids large short-lived allocations during tile-update (lower peak
heap and GC pressure).
- Prevents duplicating buffers across thread boundaries when the main
thread is behind. (one buffer per queued update, not two)

**Notes**

- after postMessage(..., [packedTileUpdates.buffer]) transfer the
worker’s packedTileUpdates becomes unusable (its .buffer is detached).


- [ ] I have added screenshots for all UI updates
- [ ] 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
- [ ] 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
2026-02-19 15:53:46 -06:00
VariableVince 7ee07fc9f0 Homepage mobile: put Solo and Ranked buttons above the public lobbies (#3245)
## Description:

Put Solo and Ranked above the public lobby buttons on mobile.

The Solo, Ranked, Create Lobby and Join Lobby buttons fall outside of
view on the average mobile screen. Since Solo is the most played game
mode and could probably be more directed to beginners, this button needs
to be in view for those who don't realize right away that there are more
buttons when scrolling down.

Ranked just has its place to the right of it and moves with it.

Create Lobby and Join Lobby are for players who already know their way
around a bit, so it's ok if they stay at the bottom. This way we
advertise having 3 public lobbies as well, as all 3 are in view always
(the 3rd at least partly which makes one curious to look what lobby it
is showing).

**BEFORE**

https://github.com/user-attachments/assets/c56e124e-069a-48bd-8860-c1113cca102f

**AFTER**

https://github.com/user-attachments/assets/83306828-d1e1-439e-9058-7f741d704ea3

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

tryout33

---------

Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-02-19 19:12:59 +00:00
FloPinguin bb1ddfbcaa Make "Not Logged In" button open Account modal & fix Account loading state (#3247)
## Description:

**TerritoryPatternsModal.ts**
- Changed the "Not Logged In" indicator from a static `div` to a
clickable `button`
- Clicking it now closes the skins modal and navigates to the Account
page via `window.showPage("page-account")`
- Added hover effect (`hover:bg-red-500/30`) for visual feedback

**AccountModal.ts**
- Fixed the inline Account modal's loading state ("Fetching account
information...") rendering without a background or header (white text on
light background 💀)
- The loading spinner is now wrapped in `modalContainerClass` (dark
glassmorphic background) with a proper `modalHeader` including the title
and back button, matching the loaded state's appearance

**SinglePlayerModal.ts**
- Changed the "Sign in for achievements" banner from a static `div` to a
clickable `button` that closes the modal and navigates to the Account
page
- Added hover effect for visual feedback

**Matchmaking.ts**
- When the "You must be logged in to play ranked matchmaking" toast
appears, the user is now also navigated to the Account page so they can
log in immediately

## 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-02-19 18:01:27 +00:00