3303 Commits

Author SHA1 Message Date
evanpelle 1e872631ca add falk2 2026-03-03 17:37:34 -08:00
evanpelle ec90332131 traefik 2026-03-03 17:37:21 -08:00
evanpelle 98ec991103 traefik 2026-03-03 11:53:59 -08:00
Skigim 17f32a590c fix: resolve drawImage scaling penalty on non-square sprite height (#3320)
## Description:

This PR resolves a performance parsing penalty in the `UnitLayer`
rendering loop by fixing two issues with non-square sprites:
1. `drawImage` was incorrectly using `sprite.width` for both width and
height, causing aspect ratio squashing and forcing the browser to
perform a scaling operation on the image instead of hitting the canvas
fast-path. It now correctly uses `sprite.height` for the vertical
dimension.
2. `clearUnitsCells` was previously configured to only clear a square
grid (`clearsize`) based solely on width, meaning taller sprites would
leave visual artifact "ghosts" on the map. The clearing bounds have been
corrected to leverage discrete `sprite.width` and `sprite.height`.
Additionally, these values are wrapped in `Math.round()` prior to offset
calculation to prevent subpixel anti-aliasing CPU penalties during
`clearRect`.

## Please complete the following:

- [x] I have added screenshots for all UI updates (not needed)
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file (no new text)
- [x] I have added relevant tests to the test directory (existing tests
suffice, change was minuscule and non-breaking)
- [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:
skigim
2026-03-02 10:56:37 -08:00
Aotumuri 98e3cd364c mls (v4.16) (#3321)
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves #(issue number)

## Description:

mls for v30
Version identifier within MLS: 4.16

## 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-03-02 10:54:37 -08:00
FloPinguin af251395bb More 2 team games on Baikal, more 4 team games on Four Islands 🎉 (#3323)
## Description:

I promised something to a redditor:

<img width="640" height="304" alt="image"
src="https://github.com/user-attachments/assets/fa319f15-f67f-486f-a5ad-598aeef7779a"
/>

### Changes

- **Map-specific team count overrides** with 75% probability:
  - **Baikal**: 2 teams (plays into the natural two-sided geography)
  - **Four Islands**: 4 teams (one team per island)
- Remaining 25% falls through to the normal weighted random team
selection
- **Doubled playlist frequency** for Baikal (5→10) and Four Islands
(4→8) in the team game playlist, making them appear twice as often

## 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-02 10:53:52 -08:00
Ryan 49b69b6fa1 [Bugfix] Force end 170mins (#3326)
## Description:

instead of just killing the server, lets save 10m before, then kill the
server at 3hr mark, so at least we have a proper savegame.

## 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-02 10:51:23 -08:00
Skigim f7598369ed refactor: consolidate platform detection across client components (#3325)
## Description:

This PR consolidates ad hoc platform/environment/viewport detection into
a single shared utility. It is scoped to this refactor only, and serves
as groundwork for the mobile-focused feature work planned for the v31
milestone.

### What changed
- Introduced a shared `Platform` utility centralising:
  - OS detection (with `userAgentData` + UA fallback)
  - Electron environment detection
- Viewport breakpoint helpers (`isMobileWidth`, `isTabletWidth`,
`isDesktopWidth`)
- Replaced duplicated inline checks across client files with the shared
API.
- Normalised Mac detection to derive from the consolidated OS logic
rather than a separate regex.

### Why
- Multiple client files each independently ran `navigator.userAgent`
regexes or copy-pasted `isElectron` logic — this unifies all of that.
- Puts a stable, tested abstraction in place before v31 mobile work
lands, so mobile feature branches have a consistent surface to build
against.

## Please complete the following:

- [x] I have added screenshots for all UI updates (N/A: refactor only,
no visible UI changes)
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file (N/A: no new user-facing strings)
- [x] I have added relevant tests to the test directory (N/A: refactor
only)
- [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:

skigim
2026-03-02 10:12:48 -08:00
dependabot[bot] 15f4f5e20a Bump rollup from 4.54.0 to 4.59.0 in the npm_and_yarn group across 1 directory (#3319)
Bumps the npm_and_yarn group with 1 update in the / directory:
[rollup](https://github.com/rollup/rollup).

Updates `rollup` from 4.54.0 to 4.59.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rollup/rollup/releases">rollup's
releases</a>.</em></p>
<blockquote>
<h2>v4.59.0</h2>
<h2>4.59.0</h2>
<p><em>2026-02-22</em></p>
<h3>Features</h3>
<ul>
<li>Throw when the generated bundle contains paths that would leave the
output directory (<a
href="https://redirect.github.com/rollup/rollup/issues/6276">#6276</a>)</li>
</ul>
<h3>Pull Requests</h3>
<ul>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6275">#6275</a>:
Validate bundle stays within output dir (<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
</ul>
<h2>v4.58.0</h2>
<h2>4.58.0</h2>
<p><em>2026-02-20</em></p>
<h3>Features</h3>
<ul>
<li>Also support <code>__NO_SIDE_EFFECTS__</code> annotation before
variable declarations declaring function expressions (<a
href="https://redirect.github.com/rollup/rollup/issues/6272">#6272</a>)</li>
</ul>
<h3>Pull Requests</h3>
<ul>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6256">#6256</a>:
docs: document PreRenderedChunk properties including isDynamicEntry and
isImplicitEntry (<a
href="https://github.com/njg7194"><code>@​njg7194</code></a>, <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6259">#6259</a>:
docs: Correct typo and improve sentence structure in docs for
<code>output.experimentalMinChunkSize</code> (<a
href="https://github.com/millerick"><code>@​millerick</code></a>, <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6260">#6260</a>:
fix(deps): update rust crate swc_compiler_base to v47 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6261">#6261</a>:
fix(deps): lock file maintenance minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6262">#6262</a>:
Avoid unnecessary cloning of the code string (<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6263">#6263</a>:
fix(deps): update minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6265">#6265</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6267">#6267</a>:
fix(deps): update minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6268">#6268</a>:
chore(deps): update dependency eslint-plugin-unicorn to v63 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6269">#6269</a>:
chore(deps): update dependency lru-cache to v11 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6270">#6270</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6272">#6272</a>:
forward NO_SIDE_EFFECTS annotations to function expressions in variable
declarations (<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
</ul>
<h2>v4.57.1</h2>
<h2>4.57.1</h2>
<p><em>2026-01-30</em></p>
<h3>Bug Fixes</h3>
<ul>
<li>Fix heap corruption issue in Windows (<a
href="https://redirect.github.com/rollup/rollup/issues/6251">#6251</a>)</li>
<li>Ensure exports of a dynamic import are fully included when called
from a try...catch (<a
href="https://redirect.github.com/rollup/rollup/issues/6254">#6254</a>)</li>
</ul>
<h3>Pull Requests</h3>
<ul>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6251">#6251</a>:
fix: Isolate and cache <code>process.report.getReport()</code> calls in
a child process for robust environment detection (<a
href="https://github.com/alan-agius4"><code>@​alan-agius4</code></a>, <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rollup/rollup/blob/master/CHANGELOG.md">rollup's
changelog</a>.</em></p>
<blockquote>
<h2>4.59.0</h2>
<p><em>2026-02-22</em></p>
<h3>Features</h3>
<ul>
<li>Throw when the generated bundle contains paths that would leave the
output directory (<a
href="https://redirect.github.com/rollup/rollup/issues/6276">#6276</a>)</li>
</ul>
<h3>Pull Requests</h3>
<ul>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6275">#6275</a>:
Validate bundle stays within output dir (<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
</ul>
<h2>4.58.0</h2>
<p><em>2026-02-20</em></p>
<h3>Features</h3>
<ul>
<li>Also support <code>__NO_SIDE_EFFECTS__</code> annotation before
variable declarations declaring function expressions (<a
href="https://redirect.github.com/rollup/rollup/issues/6272">#6272</a>)</li>
</ul>
<h3>Pull Requests</h3>
<ul>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6256">#6256</a>:
docs: document PreRenderedChunk properties including isDynamicEntry and
isImplicitEntry (<a
href="https://github.com/njg7194"><code>@​njg7194</code></a>, <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6259">#6259</a>:
docs: Correct typo and improve sentence structure in docs for
<code>output.experimentalMinChunkSize</code> (<a
href="https://github.com/millerick"><code>@​millerick</code></a>, <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6260">#6260</a>:
fix(deps): update rust crate swc_compiler_base to v47 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6261">#6261</a>:
fix(deps): lock file maintenance minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6262">#6262</a>:
Avoid unnecessary cloning of the code string (<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6263">#6263</a>:
fix(deps): update minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6265">#6265</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6267">#6267</a>:
fix(deps): update minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6268">#6268</a>:
chore(deps): update dependency eslint-plugin-unicorn to v63 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6269">#6269</a>:
chore(deps): update dependency lru-cache to v11 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6270">#6270</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6272">#6272</a>:
forward NO_SIDE_EFFECTS annotations to function expressions in variable
declarations (<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
</ul>
<h2>4.57.1</h2>
<p><em>2026-01-30</em></p>
<h3>Bug Fixes</h3>
<ul>
<li>Fix heap corruption issue in Windows (<a
href="https://redirect.github.com/rollup/rollup/issues/6251">#6251</a>)</li>
<li>Ensure exports of a dynamic import are fully included when called
from a try...catch (<a
href="https://redirect.github.com/rollup/rollup/issues/6254">#6254</a>)</li>
</ul>
<h3>Pull Requests</h3>
<ul>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6251">#6251</a>:
fix: Isolate and cache <code>process.report.getReport()</code> calls in
a child process for robust environment detection (<a
href="https://github.com/alan-agius4"><code>@​alan-agius4</code></a>, <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6252">#6252</a>:
chore(deps): update dependency lru-cache to v11 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6253">#6253</a>:
chore(deps): lock file maintenance minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6254">#6254</a>:
Fully include dynamic imports in a try-catch (<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/rollup/rollup/commit/ae846957f109690a866cc3e4c073613c338d3476"><code>ae84695</code></a>
4.59.0</li>
<li><a
href="https://github.com/rollup/rollup/commit/b39616e9175b3d9fc3977c99153174c490805a93"><code>b39616e</code></a>
Update audit-resolve</li>
<li><a
href="https://github.com/rollup/rollup/commit/c60770d7aaf750e512c1b2774989ea4596e660b2"><code>c60770d</code></a>
Validate bundle stays within output dir (<a
href="https://redirect.github.com/rollup/rollup/issues/6275">#6275</a>)</li>
<li><a
href="https://github.com/rollup/rollup/commit/33f39c1f205ea2eadaf4b589e493453e2baa3662"><code>33f39c1</code></a>
4.58.0</li>
<li><a
href="https://github.com/rollup/rollup/commit/b61c40803b717854c1c28937e8098e5ad3c7b8ca"><code>b61c408</code></a>
forward NO_SIDE_EFFECTS annotations to function expressions in variable
decla...</li>
<li><a
href="https://github.com/rollup/rollup/commit/7f00689ec90e2cafb11c26eefbcac62343c936f6"><code>7f00689</code></a>
Extend agent instructions</li>
<li><a
href="https://github.com/rollup/rollup/commit/e7b2b85af0901244ecc141b9d792c6db6b527ea4"><code>e7b2b85</code></a>
chore(deps): lock file maintenance (<a
href="https://redirect.github.com/rollup/rollup/issues/6270">#6270</a>)</li>
<li><a
href="https://github.com/rollup/rollup/commit/2aa5da9baf82211b8207d268c8751630cb766970"><code>2aa5da9</code></a>
fix(deps): update minor/patch updates (<a
href="https://redirect.github.com/rollup/rollup/issues/6267">#6267</a>)</li>
<li><a
href="https://github.com/rollup/rollup/commit/4319837c5448d0c10d89e9ded118888deec2eeec"><code>4319837</code></a>
chore(deps): update dependency lru-cache to v11 (<a
href="https://redirect.github.com/rollup/rollup/issues/6269">#6269</a>)</li>
<li><a
href="https://github.com/rollup/rollup/commit/c3b6b4bdc4f2ed978fa233132a526957e6513233"><code>c3b6b4b</code></a>
chore(deps): update dependency eslint-plugin-unicorn to v63 (<a
href="https://redirect.github.com/rollup/rollup/issues/6268">#6268</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/rollup/rollup/compare/v4.54.0...v4.59.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rollup&package-manager=npm_and_yarn&previous-version=4.54.0&new-version=4.59.0)](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-03-01 20:18:18 -08:00
scamiv 50197e7254 perf(PerformanceOverlay): reduce per-render overhead (#3295)
## Description:

Cache translated UI labels per language/translation load
Avoid per-frame layer breakdown sorting unless expanded
Use rolling sums instead of array reduce
Drop redundant requestUpdate() calls and object clones

## 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-03-01 20:15:31 -08:00
gabigabogabu 9ca342e510 Add Nile Delta map (#3306)
## Description:
Add Nile Delta as a new regional map. Features 11 nations across the
delta region (Alexandria, Damietta, Faraskur, Sheremsah, El
Senbellawein, Aga, Mit Ghamr, Cairo, Heliopolis, Memphis, El Mansoura).
1.36M land tiles at 1556x1280, terrain generated from real relief data.
Includes the Suez Canal. Playlist frequency: 4.

![Nile Delta
thumbnail](https://raw.githubusercontent.com/gabigabogabu/OpenFrontIO/feature/nile-delta-map/resources/maps/niledelta/thumbnail.webp)

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

Source: https://commons.wikimedia.org/wiki/File:Niledelta_33.svg
Discord: gabigabogabu
2026-03-01 20:14:26 -08:00
dependabot[bot] 4aa0f174ad Bump minimatch from 3.1.3 to 3.1.5 in the npm_and_yarn group across 1 directory (#3307)
Bumps the npm_and_yarn group with 1 update in the / directory:
[minimatch](https://github.com/isaacs/minimatch).

Updates `minimatch` from 3.1.3 to 3.1.5
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/isaacs/minimatch/commit/7bba97888a27a6162983056bcce2a6e28f668712"><code>7bba978</code></a>
3.1.5</li>
<li><a
href="https://github.com/isaacs/minimatch/commit/bd259425b2ca17b42897997f93e890314155522d"><code>bd25942</code></a>
docs: add warning about ReDoS</li>
<li><a
href="https://github.com/isaacs/minimatch/commit/1a9c27c75725474dbde57db2995b6281b267756d"><code>1a9c27c</code></a>
fix partial matching of globstar patterns</li>
<li><a
href="https://github.com/isaacs/minimatch/commit/1a2e084af579731af66c221214e3ca8222c9bf23"><code>1a2e084</code></a>
3.1.4</li>
<li><a
href="https://github.com/isaacs/minimatch/commit/ae24656237c3d58067442f790ce17eff84463a47"><code>ae24656</code></a>
update lockfile</li>
<li><a
href="https://github.com/isaacs/minimatch/commit/b1003749228b2a79e1f237963a0d559ef7a0941e"><code>b100374</code></a>
limit recursion for **, improve perf considerably</li>
<li><a
href="https://github.com/isaacs/minimatch/commit/26ffeaa091b9f660833e23f42e07165b33e85c13"><code>26ffeaa</code></a>
lockfile update</li>
<li><a
href="https://github.com/isaacs/minimatch/commit/9eca892a4e5dbb20534f9f30483b85cdeee6c2eb"><code>9eca892</code></a>
lock node version to 14</li>
<li>See full diff in <a
href="https://github.com/isaacs/minimatch/compare/v3.1.3...v3.1.5">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.3&new-version=3.1.5)](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-03-01 20:13:04 -08:00
FloPinguin 417fa0fe09 For v30: Add new modifiers (Hard nations and 25M Starting Gold) 🙂 (#3316)
## Description:

Adds two new public game modifiers for variety and improves compact map
eligibility for team games.

### New Modifiers

**Hard Nations (`isHardNations`)**
- We need this modifier for HvN, because medium nations are easier now
(will result in a much higher human winrate)
- In a discord discussion we concluded that HvN should generally be
easier (higher winrate than 50%, so players are less frustated)
- Thats why only 20% of HvN games have the hard nations modifier (for
now)
- For PvPvE enjoyers, the modifier is also active in FFA games => (Only
2.5% chance, and 1 ticket in `SPECIAL_MODIFIER_POOL`)

**25M Starting Gold (`startingGoldHigh`)**
- Some people in the main discord wanted this modifier, and it will
result in crazy games
- Rare special-only modifier (1 ticket in pool); mutually exclusive with
5M starting gold via `MUTUALLY_EXCLUSIVE_MODIFIERS`
- Disables nations (they lack PVP immunity, so 25M gold doesn't work
well with them)
- Excluded from HumansVsNations games (since it disables nations)
- Spawn immunity set to **2 minutes 30 seconds** (vs 30s for 5M gold),
so people can spend the gold and prepare

### Other Changes

- **Improved `supportsCompactMapForTeams`**: Replaced the hard `smallest
>= 50` land-tile cutoff with a per-team-config calculation that
simulates worst-case compact player count and checks every team gets at
least 2 players.
- **HvN spawn immunity**: Always 5 seconds in both regular and special
lobbies (to get rid of a confusing PVP immunity HeadsUpMessage in 5M
starting gold games)
- **Regular public lobby random spawn modifier probabilty**: Reduced
from 10% to 5% (Because of the new modifier, so there aren't too many
modifiers in non-special-lobbies, should only occur sometimes there)
- Rebalanced `SPECIAL_MODIFIER_POOL` a bit

## 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-01 20:12:38 -08:00
Mattia Migliorini e1125e0c37 Fix: Nations reject alliance requests created pre-spawn (#3314)
## Description:

This PR fixes an exploit that allows the player to request alliances to
Nations, mostly in impossible mode, during spawn phase, with high
chances for it to be accepted due to troop count parity.

Nations now reject alliance requests during the spawn phase.

`GameImpl.executeNextTick()` initializes ALL pending `unInitExecs` in
one batch on the first post-spawn tick ( `numSpawnPhaseTurns() + 1` ).
So every alliance request submitted during spawn phase is guaranteed to
be created with `createdAt = numSpawnPhaseTurns() + 1` on the very first
post-spawn tick.
Therefore, we check for alliance requests created on the very first
post-spawn tick and reject those.

## 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-03-01 21:33:41 +00:00
Ryan 802cc7f16d Revert "Fix: Nations reject alliance requests during spawn phase" (#3313)
## Description:

Reverts openfrontio/OpenFrontIO#3312


## 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-01 12:11:00 +00:00
Mattia Migliorini a9c89e4f15 Fix: Nations reject alliance requests during spawn phase (#3312)
## Description:

This PR fixes an exploit that allows the player to request alliances to
Nations, mostly in impossible mode, during spawn phase, with high
chances for it to be accepted due to troop count parity.

Nations now reject alliance requests during the spawn phase.

## 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-03-01 11:20:19 +00:00
bijx 8754f5291f Feat: Alphanumeric Coordinate Grid on Alternate View (#2938)
## Description:

Adds a coordinate grid to the Alternate View (holding spacebar) using
numbers on the X-axis, and letters on the Y-axis. No more "he's
attacking you in that—well, the little peninsula thing... next to the
island! which island? uhh..." moments when playing with friends.
Optimally maps have letters A-J (just like in the Battleships board
game) but special maps like Amazon River dynamically resize to only have
2 letters so as to not have too many number columns. This feature
overall can be toggled via the settings menu.

Also saw it requested on the [official
discord](https://discord.com/channels/1359946986937258015/1457037351422263480)
a couple times, thought it was a neat idea.

### World Map
<img width="3809" height="1824" alt="image"
src="https://github.com/user-attachments/assets/dab56879-a34e-48ea-a588-2907d26feb45"
/>

### Scales correctly when zoomed in
<img width="3798" height="1874" alt="image"
src="https://github.com/user-attachments/assets/7e06a47f-d3d9-4f92-8e89-3eaf866e9b25"
/>

### Amazon River
<img width="3803" height="1595" alt="image"
src="https://github.com/user-attachments/assets/4797c576-20b2-4aa8-8b7a-107078ab6308"
/>

### Enable/Disable via settings


https://github.com/user-attachments/assets/ec9f4e07-70a1-4f9d-b137-c3c3d2a2540c

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

---------

Co-authored-by: iamlewis <lewismmmm@gmail.com>
2026-02-28 20:28:47 -08:00
Ryan ebe1f76bbf improved configuration handling, including special lobby (#3224)
## Description:

Special games now get a random map from a dedicated pool (which includes
arcade maps that are excluded from regular FFA/team rotations), a 50/50
chance of FFA or Team mode, and are guaranteed to have at least one
modifier active, compact map, random spawn, crowded, or 5m starting gold
, with higher roll rates than normal 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:

w.o.n
2026-02-28 20:24:07 -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
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