Commit Graph

3339 Commits

Author SHA1 Message Date
evanpelle eeb75342f6 improve game starting modal styling 2026-03-08 22:04:22 -07:00
Evan c63b304a97 various homepage improvements (#3387)
## Description:

Various changes, applied more styling from the homewrecker branch

* dimmed background
* Content width: expands to 24cm on 2xl screens
* game card ocean color: French blue → sky-950
* Action buttons (Create/Ranked/Join): French blue → slate-700
* Modifier badges: teal → sky blue, to keep in color scheme
* CTA buttons (Start Game, Join Lobby): blue-600 → sky-600 across all
modals and <o-button>
* Nav font: font-bold tracking-widest → font-medium tracking-wider
* Username/flag inputs: font weight lightened to font-medium
tracking-wider
* Language flag: blue color filter applied


BEFORE:


<img width="1446" height="978" alt="Screenshot 2026-03-08 at 6 48 57 PM"
src="https://github.com/user-attachments/assets/ff748e1c-6cb5-4a66-ac27-9538e935b325"
/>

AFTER:

<img width="1629" height="988" alt="Screenshot 2026-03-08 at 6 46 53 PM"
src="https://github.com/user-attachments/assets/364bb57a-65ff-40cf-931b-067ed36e3c5b"
/>


## Please complete the following:


- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

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

evan
2026-03-08 19:00:24 -07:00
RickD004 df62251d3a Add San Francisco map (#3373)
## Description:

Adds San Francisco bay map. 21 nations based on cities and towns of the
area. 1.8M land pixels , size of 2000x1700
Elevation data from Opentopography, already credited. 
Map frequency of 3, as to mirror New York map.

<img width="2000" height="1700" alt="image"
src="https://github.com/user-attachments/assets/dc80a2db-6233-4b50-8f07-bd21c23c8b53"
/>

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

tri.star1011
2026-03-07 20:51:59 -08:00
Damien Gustave b648b102fa feat(chat): add stop trading with all chat option (#3362)
## Description:

Adds a quick chat option to ask a player to stop trading with all. I
wrote it in double quote to inform this is an option as I think most of
the players do not even know about this option.

<img width="892" height="554" alt="image"
src="https://github.com/user-attachments/assets/5a9330f4-e07c-4dd7-a1f2-a1e0e6508f90"
/>

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

```
.delovan
```
2026-03-07 20:38:37 -08:00
진윤 a4b3d13c86 remove Rising sun flag (#3377)
## Description:

This PR removes the Rising Sun Flag assets to align with the game's
policy on hate symbols, as discussed in the Discord channel.

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

jinyoon

Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
2026-03-08 00:17:34 +00:00
Bartosz Woźniak 936928fed9 Enhance InputHandler to allow using NumPad (#3317)
## Description:

Adds **Enter** and **Numpad Enter** as confirmation for placing a ghost
structure after selecting a building with hotkeys (1–0 or numpad).
Players can cancel with Esc but previously had to click to confirm; they
can now confirm with Enter or Numpad Enter at the current cursor
position. This supports keyboard-only or mouse + numpad workflows (e.g.
one hand on numpad for select + confirm, one on mouse for aiming).

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

.wozniakpl
2026-03-07 15:27:25 -08:00
FloPinguin fe89713f46 Fix UI (again) 🖌️ (#3379)
## Description:

 **Fix UI spacing and border radius across multiple screen sizes**

- Fix events panel missing right margin on wide screens
- Fix incorrect border radius on events panel and control panel at
various breakpoints
- Remove border radius from attack/boat elements on small screens
- Show running attacks above the events panel on mobile
- Add left/right margin to the homepage on tablet-sized screens
- Adjust lobby card spacing on mobile

Previous

<img width="410" height="124" alt="Screenshot 2026-03-07 203244"
src="https://github.com/user-attachments/assets/d3feb9fe-97a3-44d0-9aba-db04062f9911"
/>


After

<img width="417" height="117" alt="Screenshot 2026-03-07 203255"
src="https://github.com/user-attachments/assets/31b88145-8e92-40db-b9cc-f2a00754f900"
/>


Previous

<img width="828" height="123" alt="Screenshot 2026-03-07 203320"
src="https://github.com/user-attachments/assets/4e162cf5-7d82-4e87-9dd9-9ab1d3782f23"
/>


After

<img width="820" height="126" alt="Screenshot 2026-03-07 203337"
src="https://github.com/user-attachments/assets/a25121aa-603c-41c7-b335-406a38a62cf9"
/>


Previous

<img width="961" height="102" alt="Screenshot 2026-03-07 203353"
src="https://github.com/user-attachments/assets/22ba9770-88a3-4f49-aeb6-6d875006946b"
/>


After

<img width="954" height="78" alt="Screenshot 2026-03-07 203403"
src="https://github.com/user-attachments/assets/0d4e3b19-de1c-4211-b1e3-bd935025de33"
/>


Previous

<img width="557" height="154" alt="Screenshot 2026-03-07 203450"
src="https://github.com/user-attachments/assets/2cc8a747-3e68-4449-9746-62fcbca76510"
/>


After

<img width="602" height="146" alt="Screenshot 2026-03-07 203421"
src="https://github.com/user-attachments/assets/bae399a3-8969-4b7a-a77c-c73c4f775ca0"
/>


Previous

<img width="727" height="889" alt="Screenshot 2026-03-07 204707"
src="https://github.com/user-attachments/assets/bc53febf-9beb-4195-a994-858333f30f24"
/>


After

<img width="725" height="799" alt="Screenshot 2026-03-07 204714"
src="https://github.com/user-attachments/assets/9d600212-73ae-4566-b1c5-df83e8edb8e9"
/>


Previous

<img width="658" height="890" alt="Screenshot 2026-03-07 204633"
src="https://github.com/user-attachments/assets/6c935fcc-3e46-4706-8c9a-9840cc469b60"
/>


After

<img width="656" height="798" alt="Screenshot 2026-03-07 204639"
src="https://github.com/user-attachments/assets/8e490f29-cf50-4c1f-a97e-f550fd4f9a13"
/>

## 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-07 13:01:18 -08:00
FloPinguin 526cb723d6 Fix 2 HvN UI bugs 🔧 (#3378)
## Description:

I noticed two HvN bugs.

1. Private lobbies don't set `maxPlayers` in `GameConfig`, causing
`getGameModeLabel()` to render "0 Humans vs 0 Nations". Fall back to the
simple "Humans vs Nations" label when `maxPlayers` is unavailable.

<img width="239" height="84" alt="Screenshot 2026-03-07 034150"
src="https://github.com/user-attachments/assets/b2f01b96-674f-47dc-ae03-06bec71e3134"
/>

2. In public HumansVsNations games, the server matches the nation count
to the human player count at game start. The lobby team size preview
wasn't reflecting this - it displayed the raw config value instead.
Added `isPublicGame` prop to `LobbyPlayerView` and an
`effectiveNationCount` getter that overrides the displayed nation count
to match `clients.length` only for public HvN games. Private lobby hosts
retain full slider control. (This bug got introduced with my
"Configurable nation count" PR)

## Please complete the following:

- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [X] I have added relevant tests to the test directory
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

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

FloPinguin
2026-03-07 12:57:16 -08:00
FloPinguin 3fca25f421 Skip multi-tab detection during replays 🛠️ (#3366)
## Description:

Multi-tab detection was incorrectly penalizing users watching replays.
Added `isReplay()` check to `MultiTabModal.tick()` so the detector is
never initialized when viewing a replay.

## 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-06 20:02:03 -08:00
dependabot[bot] 73ad08f0cf Build(deps): bump dompurify from 3.2.6 to 3.3.2 in the npm_and_yarn group across 1 directory (#3365)
Bumps the npm_and_yarn group with 1 update in the / directory:
[dompurify](https://github.com/cure53/DOMPurify).

Updates `dompurify` from 3.2.6 to 3.3.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/cure53/DOMPurify/releases">dompurify's
releases</a>.</em></p>
<blockquote>
<h2>DOMPurify 3.3.2</h2>
<ul>
<li>Fixed a possible bypass caused by jsdom's faulty raw-text tag
parsing, thanks multiple reporters</li>
<li>Fixed a prototype pollution issue when working with custom elements,
thanks <a
href="https://github.com/christos-eth"><code>@​christos-eth</code></a></li>
<li>Fixed a lenient config parsing in <code>_isValidAttribute</code>,
thanks <a
href="https://github.com/christos-eth"><code>@​christos-eth</code></a></li>
<li>Bumped and removed several dependencies, thanks <a
href="https://github.com/Rotzbua"><code>@​Rotzbua</code></a></li>
<li>Fixed the test suite after bumping dependencies, thanks <a
href="https://github.com/Rotzbua"><code>@​Rotzbua</code></a></li>
</ul>
<h2>DOMPurify 3.3.1</h2>
<ul>
<li>Updated <code>ADD_FORBID_CONTENTS</code> setting to extend default
list, thanks <a
href="https://github.com/MariusRumpf"><code>@​MariusRumpf</code></a></li>
<li>Updated the ESM import syntax to be more correct, thanks <a
href="https://github.com/binhpv"><code>@​binhpv</code></a></li>
</ul>
<h2>DOMPurify 3.3.0</h2>
<ul>
<li>Added the SVG <code>mask-type</code> attribute to default
allow-list, thanks <a
href="https://github.com/prasadrajandran"><code>@​prasadrajandran</code></a></li>
<li>Added support for <code>ADD_ATTR</code> and <code>ADD_TAGS</code> to
accept functions, thanks <a
href="https://github.com/nelstrom"><code>@​nelstrom</code></a></li>
<li>Fixed an issue with the <code>slot</code> element being in both SVG
and HTML allow-list, thanks <a
href="https://github.com/Wim-Valgaeren"><code>@​Wim-Valgaeren</code></a></li>
</ul>
<h2>DOMPurify 3.2.7</h2>
<ul>
<li>Added new attributes and elements to default allow-list, thanks <a
href="https://github.com/elrion018"><code>@​elrion018</code></a></li>
<li>Added <code>tagName</code> parameter to custom element
<code>attributeNameCheck</code>, thanks <a
href="https://github.com/nelstrom"><code>@​nelstrom</code></a></li>
<li>Added better check for animated <code>href</code> attributes, thanks
<a href="https://github.com/llamakko"><code>@​llamakko</code></a></li>
<li>Updated and improved the bundled types, thanks <a
href="https://github.com/ssi02014"><code>@​ssi02014</code></a></li>
<li>Updated several tests to better align with new browser encoding
behaviors</li>
<li>Improved the handling of potentially risky content inside CDATA
elements, thanks <a
href="https://github.com/securityMB"><code>@​securityMB</code></a> &amp;
<a href="https://github.com/terjanq"><code>@​terjanq</code></a></li>
<li>Improved the regular expression for raw-text elements to cover
textareas, thanks <a
href="https://github.com/securityMB"><code>@​securityMB</code></a> &amp;
<a href="https://github.com/terjanq"><code>@​terjanq</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/cure53/DOMPurify/commit/5e56114cb24079ce52dbc51f76e494b77afa5153"><code>5e56114</code></a>
Getting 3.x branch ready for 3.3.2 release (<a
href="https://redirect.github.com/cure53/DOMPurify/issues/1208">#1208</a>)</li>
<li><a
href="https://github.com/cure53/DOMPurify/commit/e8c95f4a27aa8b041f92b59ab7685a94f7be6208"><code>e8c95f4</code></a>
fix: Fixed the broken package-lock.json</li>
<li><a
href="https://github.com/cure53/DOMPurify/commit/9636037c145b769dad0b52da8313301cbf867f46"><code>9636037</code></a>
Update package-lock.json</li>
<li><a
href="https://github.com/cure53/DOMPurify/commit/5cad4cecf2e647ac66eed25bc02a2415f00dbc8b"><code>5cad4ce</code></a>
Getting 3.x branch ready for 3.3.2 releas (<a
href="https://redirect.github.com/cure53/DOMPurify/issues/1205">#1205</a>)</li>
<li><a
href="https://github.com/cure53/DOMPurify/commit/6fc446a589ab3d1d72ae2a5b71167ba38dbd3096"><code>6fc446a</code></a>
Merge pull request <a
href="https://redirect.github.com/cure53/DOMPurify/issues/1175">#1175</a>
from cure53/main</li>
<li><a
href="https://github.com/cure53/DOMPurify/commit/3b3bf917d2b39460de6d130acebdc9243cf3e6ae"><code>3b3bf91</code></a>
Merge branch 'main' of github.com:cure53/DOMPurify</li>
<li><a
href="https://github.com/cure53/DOMPurify/commit/9863f4195bae6048de9eb2802219218c6904066c"><code>9863f41</code></a>
chore: Preparing 3.3.1 release</li>
<li><a
href="https://github.com/cure53/DOMPurify/commit/b4e02954dc4172c3944a755f3e99fbb76be64f7b"><code>b4e0295</code></a>
chore: Preparing 3.3.0 release</li>
<li><a
href="https://github.com/cure53/DOMPurify/commit/077746bb2cfb77836dfb628dca7ffc7ced8a5356"><code>077746b</code></a>
build(deps-dev): bump js-yaml from 4.1.0 to 4.1.1 (<a
href="https://redirect.github.com/cure53/DOMPurify/issues/1170">#1170</a>)</li>
<li><a
href="https://github.com/cure53/DOMPurify/commit/4de68bba9aba43dc3bba9348df603b64fc06d591"><code>4de68bb</code></a>
build(deps): bump actions/checkout from 5 to 6 (<a
href="https://redirect.github.com/cure53/DOMPurify/issues/1171">#1171</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/cure53/DOMPurify/compare/3.2.6...3.3.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dompurify&package-manager=npm_and_yarn&previous-version=3.2.6&new-version=3.3.2)](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-06 20:01:40 -08:00
DevelopingTom 902a0b42ac Remove useless sprite setting (#3363)
## Description:

Redundant animated sprite setting: the sprite frame width can be
computed directly.

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

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

IngloriousTom
2026-03-06 19:58:14 -08:00
Evan 815f1de67b Update control panel UI (#3357)
Relates to #2260

## Description:

Inspired by https://github.com/openfrontio/OpenFrontIO/pull/3359

This PR centers the control panel and combines it with the units
display. The reasoning is that the control panel contains the most
critical info so it should be in the center of the screen. Combining it
with the units display reduces the number of UI components on screen.

Also made the attack ratio bar persistent on mobile

<img width="618" height="216" alt="Screenshot 2026-03-06 at 2 06 34 PM"
src="https://github.com/user-attachments/assets/34b041c1-d78b-46b5-a7ab-f2a44145a7e2"
/>


<img width="941" height="343" alt="Screenshot 2026-03-06 at 2 06 55 PM"
src="https://github.com/user-attachments/assets/1e3b026c-8eb2-407c-be38-0e71e1ae426c"
/>

<img width="562" height="228" alt="Screenshot 2026-03-06 at 4 11 20 PM"
src="https://github.com/user-attachments/assets/56eac49f-c8a4-4ac1-a60a-f1bcb2fad2d0"
/>

<img width="939" height="357" alt="Screenshot 2026-03-06 at 4 11 32 PM"
src="https://github.com/user-attachments/assets/eb5591d5-3cc2-4182-944b-3a4b0b76852a"
/>


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

Co-authored-by: hkio120 <111693579+hkio120@users.noreply.github.com>
2026-03-06 18:32:01 -08:00
Ryan 0eb23c0c8c clientId replay bugfix (was picking first clientID in the array) (#3369)
## Description:

clientId replay bugfix (was picking first clientID in the array)

https://discord.com/channels/1359946986937258015/1479543573404844042

## 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-06 13:58:10 -08:00
dependabot[bot] 5594109641 Build(deps): bump fast-xml-parser from 5.3.6 to 5.4.1 in the npm_and_yarn group across 1 directory (#3347)
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.6 to 5.4.1
<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>Separate Builder</h2>
<p>XML Builder was the part of <a
href="https://github.com/NaturalIntelligence/fast-xml-builder">fast-xml-parser</a>
for years. But considering that any bug in builder may false-alarm the
users who are only using parser and vice-versa, we have decided to split
it into a separate package.</p>
<h2>Migration</h2>
<p>To migrate to fast-xml-builder;</p>
<p>From</p>
<pre lang="js"><code>import { XMLBuilder } from
&quot;fast-xml-parser&quot;;
</code></pre>
<p>To</p>
<pre lang="js"><code>import XMLBuilder from
&quot;fast-xml-builder&quot;;
</code></pre>
<p>XMLBuilder will be removed from current package in any next major
version of this library. So better to migrate.</p>
<h2>support strictReservedNames</h2>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.9...v5.3.9">https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.9...v5.3.9</a></p>
<h2>handle non-array input for XML builder &amp;&amp; support
maxNestedTags</h2>
<ul>
<li>support maxNestedTags</li>
<li>handle non-array input for XML builder when preserveOrder is true
(By <a href="https://github.com/Angelopvtac">Angelo Coetzee</a>)</li>
<li>save use of js properies
<strong>Full Changelog</strong>: <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.7...v5.3.8">https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.7...v5.3.8</a></li>
</ul>
<h2>CJS typing fix</h2>
<h2>What's Changed</h2>
<ul>
<li>Unexport <code>X2jOptions</code> at declaration site by <a
href="https://github.com/Drarig29"><code>@​Drarig29</code></a> in <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/pull/787">NaturalIntelligence/fast-xml-parser#787</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/Drarig29"><code>@​Drarig29</code></a>
made their first contribution in <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/pull/787">NaturalIntelligence/fast-xml-parser#787</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.6...v5.3.7">https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.6...v5.3.7</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>Note: Due to some last quick changes on v4, detail of v4.5.3 &amp;
v4.5.4 are not updated here. v4.5.4x is the last tag of v4 in github
repository. I'm extremely sorry for the confusion</p>
<p><strong>5.4.2 / 2026-03-03</strong></p>
<ul>
<li>support maxEntityCount option</li>
</ul>
<p><strong>5.4.1  / 2026-02-25</strong></p>
<ul>
<li>fix (<a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/785">#785</a>)
unpairedTag node should not have tag content</li>
</ul>
<p><strong>5.4.0  / 2026-02-25</strong></p>
<ul>
<li>migrate to fast-xml-builder</li>
</ul>
<p><strong>5.3.9 / 2026-02-25</strong></p>
<ul>
<li>support strictReservedNames</li>
</ul>
<p><strong>5.3.8 / 2026-02-25</strong></p>
<ul>
<li>support maxNestedTags</li>
<li>handle non-array input for XML builder when preserveOrder is true
(By <a href="https://github.com/Angelopvtac">Angelo Coetzee</a>)</li>
<li>save use of js properies</li>
</ul>
<p><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>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/4e7ca80e788a23b07531ac2ff8906e5e9f4bf892"><code>4e7ca80</code></a>
update release info</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/36023b496382717c82bd68863b3f95629d0c9311"><code>36023b4</code></a>
fix (<a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/785">#785</a>)
unpairedTag node should not have tag content</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/b3660266f53e383193ae152cde878d9b2db7240f"><code>b366026</code></a>
separate builder</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/6f333a85693e20713fea2d733795fef7e11ac51c"><code>6f333a8</code></a>
update release info</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/c3ffbab9e5a2bab9db65803933d0af656076fc33"><code>c3ffbab</code></a>
support strictReservedNames</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/c692040f6b5f5045d38b66b1da04e4d3abc97052"><code>c692040</code></a>
update release info</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/107e34c046d4997ee3b67a32d32eef52fe63edb2"><code>107e34c</code></a>
avoid <code>{}</code> to create an empty object</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/60835a4c7279ddc349d192097fb41afa52930d8b"><code>60835a4</code></a>
support maxNestedTags</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/f55657c2b1cf29b433124390c32acba45a5a67aa"><code>f55657c</code></a>
avoid direct call to hasOwnProperty</li>
<li><a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commit/c13a961910f14986295dd28484eee830fa1a0e8a"><code>c13a961</code></a>
handle non-array input for XML builder when preserveOrder is true</li>
<li>Additional commits viewable in <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.6...v5.4.1">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.6&new-version=5.4.1)](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>
Co-authored-by: Evan <evanpelle@gmail.com>
2026-03-05 20:48:15 -08:00
FloPinguin c594487f5e Starting gold input in millions with decimal support (#3349)
## Description:

**Starting gold input: use millions**

Changes the starting gold input in singleplayer and host lobby modals to
accept values in millions (e.g. enter `5` for 5M gold). Supports
decimals like `6.6` for 6.6M. The value is multiplied by 1,000,000
before being sent to the game config.

- Label updated to "Starting Gold (Millions)"
- Input uses float parsing with min 0.1, matching gold multiplier
behavior
- JoinLobbyModal shows clean values without unnecessary decimals (e.g.
"5M" not "5.00M")

Previous

<img width="215" height="139" alt="image"
src="https://github.com/user-attachments/assets/00ce5b6d-f74d-4aee-92f5-c9be1a0a6d3d"
/>
<img width="292" height="74" alt="image"
src="https://github.com/user-attachments/assets/4de936a3-22bd-4ffc-8dbe-0d5066f28186"
/>

Now

<img width="216" height="151" alt="image"
src="https://github.com/user-attachments/assets/489de13e-65b5-4b02-a654-5f6f74b165d1"
/>
<img width="292" height="72" alt="image"
src="https://github.com/user-attachments/assets/51723d5a-55ab-4b7b-bbce-011a586eeb44"
/>

## 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-05 20:48:03 -08:00
RickD004 6254cc0598 Add Arctic map (#3341)
## Description:

Adds Arctic map, a map centered about the Geographical North Pole using
an azimuthal equidistant projection. Features Cold War themed countries
and subdivisions as nations. Square map with 1.6M land tiles. Terrain
data from Opentopography and Arctic SDI real relief data

<img width="1830" height="1830" alt="image"
src="https://github.com/user-attachments/assets/0b8b1e42-f477-4ebf-a256-c07536db87d9"
/>

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

tri.star1011
2026-03-05 20:43:34 -08:00
FloPinguin 0dc520b1c8 Add confirmation dialog before closing host lobby modal 🔧 (#3364)
## Description:

Show a confirm prompt when the user tries to close the host lobby via
click-outside or Escape key, preventing accidental lobby exits. Happened
to many people and also DougDoug...

Does not show the prompt when the user clicks the arrow button because
it's probably intentional.

## 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-05 20:39:22 -08:00
FloPinguin 6154b779f5 Fix compact map playercount 🔧 (#3361)
## Description:

The recently improved `supportsCompactMapForTeams` only checked that
each team had ≥2 players but not that there were ≥2 teams. On very small
maps with compact + Duos (rare), this allowed lobbies with 1 team of 2,
which makes no sense (Wonder noticed that randomly while looking at the
homepage). Added a `numberOfTeams ≥ 2` check so the compact map modifier
isn't active when the player count can't sustain multiple teams.

The 125-player performance cap was also applied inside
`calculateMapPlayerCounts` before the compact 75% reduction, so a map
sized for 200 players would get capped to 125 then reduced to 31 instead
of the expected 50. Moved the cap (`MAX_PLAYER_COUNT`) to after the
compact reduction.

## 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-05 15:21:35 -08:00
Evan 0733c680b9 homepage UI improvements (#3352)
## Description:

A bunch of small UI improvements:

* Make the content width a bit smaller so gutter ads fit
* remove the "duos" "trios" "quads" description on the game card since
it's redundant
* update UI in game card
* minor footer layout changes
* update z-index to ensure content appears above ads
* removed hasUnusualThumbnailSize, instead just check the map ratio
* Use "object cover" for non-irregular maps to the entire game card is
filed
* remove white ouline from the version
* changed solo button to sky blue
* make timer "s" lowercase


I think we may need to change the openfront logo color a bit too to
match the color palette, but we can do that in a follow up.

<img width="1591" height="969" alt="Screenshot 2026-03-05 at 2 04 48 PM"
src="https://github.com/user-attachments/assets/7bb9ea4c-5a17-47e1-bdad-9d6437b363b3"
/>


## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

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

evan
2026-03-05 15:17:28 -08:00
Evan 47ef5a032b update favicon to blue warship icon (#3360)
## Description:

light mode:
<img width="160" height="31" alt="Screenshot 2026-03-05 at 1 44 26 PM"
src="https://github.com/user-attachments/assets/2b80979c-9a4a-45c2-9fbc-bd6e48ddd823"
/>

dark mode:
<img width="172" height="50" alt="Screenshot 2026-03-05 at 1 44 34 PM"
src="https://github.com/user-attachments/assets/2cfa12cb-cd8e-4466-a2e2-1ccb9b7f2538"
/>

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

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

evan
2026-03-05 13:47:28 -08:00
Ryan b3c01d4c85 improve streamer mode (#3353)
## Description:

improves streamer mode (doesn't show the gameID in the url, it just says
"streamer-mode"


## 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-04 19:27:53 -08:00
FloPinguin cf3e3a7a74 Fix bot/nation player ID collisions causing missing players 🔧 (#3354)
## Description:

BotSpawner used the same PRNG seed (simpleHash(gameID)) as
createGameRunner, causing bot IDs to collide with nation IDs. When a
bot's SpawnExecution found a nation with the same ID via hasPlayer(), it
silently reused that nation instead of creating a new player - resulting
in far fewer players than configured (e.g. ~670 instead of 800 with 400
bots + 400 nations) with no console warnings.

Offsets the BotSpawner seed by +2 to avoid the shared PRNG sequence
(matching the +1 pattern already used by Executor).

## 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-04 16:54:29 -08:00
VariableVince e137fcaa6c Fix/Perf/Refactor: playerActions and buildableUnits, their callers and related types (#3220)
## Description:

TL;DR: it's faster.

buildableUnits is called via PlayerView.actions from UnitDisplay (each
tick without TileRef), BuildMenu (each tick when open), MainRadialMenu
(each tick when open), PlayerPanel (each tick when open),
StructureIconsLayer (when placing a building from build bar),
NukeTrajectoryPreviewLayer (when placing nuke, on tick when tile
changes), ClientGameRunner (on click to attack/auto-boat or hotkey B or
G).

After https://github.com/openfrontio/OpenFrontIO/pull/3213 got merged,
the change with largest impact in
https://github.com/openfrontio/OpenFrontIO/pull/3193 was done in such a
different way that a new PR was needed

The idea in 3193 was to not always ask for Transport Ship from
buildableUnits. In such a way that very little extra data was send to
the worker. This had the biggest impact on performance (the idea was
months older btw, see
https://github.com/openfrontio/OpenFrontIO/pull/2295). Now, we do it the
other way around, by telling buildableUnits all unit types we want. Or
we want them all (undefined). The downside is more data is send in the
worker message. The upside is we have more options and can add more in
this PR.

This PR implements some of the leftovers in 3193 on top of 3213 and adds
further improvements.

(Some unrelated refactor/perf changes where moved out of this PR and
into already merged
https://github.com/openfrontio/OpenFrontIO/pull/3233,
https://github.com/openfrontio/OpenFrontIO/pull/3234,
https://github.com/openfrontio/OpenFrontIO/pull/3235,
https://github.com/openfrontio/OpenFrontIO/pull/3236,
https://github.com/openfrontio/OpenFrontIO/pull/3237,
https://github.com/openfrontio/OpenFrontIO/pull/3238,
https://github.com/openfrontio/OpenFrontIO/pull/3239)

- **GameRunner**, **WorkerMessages**: _playerActions_ and
_PlayerActionsMessage ._ Option to ask for no buildable units (null). It
now has 3 modes: get all actions and all buildings (units undefined),
get all actions and no buildings (units null), or get all actions and
specific building (units contains Unit Types).

- **GameRunner**: _playerActions_. fixes wrong assumption in PR 3213:
that only if units was undefined, we have to know canAttack.
ClientGameRunner wants to know both, in case of a click on non-bordering
land, to decide if it should auto-boat using a Transport Ship. So units
is not undefined (we only ask for Transport Ship now which has a
positive effect on performance for each click/tap) but we need canAttack
still.
Solved by removing the unit === undefined check before _canAttack_ in
_playerActions_.

- **GameRunner**, **GameView**, **WorkerClient**, **WorkerMessages**,
**Worker.worker**: added _playerBuildables_ / _buildables_ next to
existing _playerActions_ / _actions_. With above solved, there was still
no option to only get buildable units when the actions are not needed.
While **StructureIconsLayer**, **NukeTrajectoryPreviewLayer**,
**BuildMenu** and **UnitDisplay** need only that. To not make
playerActions more convoluted with more params or so, i've added a new
function _playerBuildables_ in **GameView** to only get buildable units
(**GameRunner** _playerBuildables_). _playerBuildables_ has 2 modes: get
all buildings (units undefined) or get specific buildings (units
contains Unit Types). Also update some comments that mentioned .actions
in **NukeTrajectoryPreviewLayer**.

- **ClientGameRunner**, **PlayerPanel**, **BuildMenu**, **UnitDisplay**,
**StructureIconsLayer** and **NukeTrajectoryPreviewLayer**: Since PR
3213, **StructureIconsLayer** and **NukeTrajectoryPreviewLayer** ask for
specific types of units from **GameView** _actions_ (**GameRunner**
playerActions). Now have the other files do the same. For example
**BuildMenu** asks for the new _BuildMenuTypes_ when it calls
._buildables_ and **ClientGameRunner** asks for UnitType.TransportShip
when sending a boat

- **ClientGameRunner**: canBoatAttack now accepts BuildableUnit[]
instead of PlayerActions so we can send it either actions.buildableUnits
or just buildables. Have functions call myPlayer.buildables(tileRef,
[UnitType.TransportShip]) when we only need a buildable unit and no
actions. Or myPlayer.actions(tileRef, null) when we need actions but no
buildable units. Or myPlayer.actions(tileRef, [UnitType.TransportShip])
when we need both actions, like canAttack, and a buildable unit. Then if
needed send either actions.buildableUnits or buildables to to
_canAutoBoat_ / _canBoatAttack_.

- **MainRadialMenu**: needs all player buildable unit types including
Transport Ship, so the _actions_ call argument for unit types can stay
undefined (unchanged) there.

- **MainRadialMenu**: now that **BuildMenu** uses _playerBuildables_
instead of _playerActions_, we must put data in
_this.buildMenu.playerBuildables_. And since we're not putting the
(unneeded) full _actions_ in there anymore, we can now put only the
needed and expected _actions._buildableUnits_ in it.

- **Game**, **PlayerImpl**, **StructureIconsLayer**: Typesafety and some
added perf: new type _PlayerBuildableUnitType_ (see also the below point
for how it is formed). So callers of _buildableUnits_ can never ask for
the wrong type like e.g. UnitType.Train because it doesn't return data
for that type. This type is now used in **PlayerImpl**, **BuildMenu**,
**RadialMenuElements**, **StructureDrawingUtils** and **UnitDisplay**
for that reason. And **InputHandler**, **StructureIconsLayer** and
**UIState** (little more on that in point below).

- **InputHandler**, **StructureIconsLayer**, **UIState**: In order to
make type safety work for GhostUnit.buildableUnit.type too (line ~217 of
StructureIconsLayer), changed type of interface _BuildableUnit_ to
_PlayerBuildableType_. Which is only more accurate. Same for and
this.structures and uiState.ghostStructure and with the latter,
_renderUnitItem_ in **UnitDisplay** and _setGhostStructure_ in
**InputHandler**. All Structures are of PlayerBuildableType (there are
even some in PlayerBuildables that aren't Structures, but it is much
more confined than UnitType).

- **Game**: Typesafety and some added perf: added _BuildMenus_ and
_BuildableAttacks_ in the same fashion that the existing StructureTypes
was already used (simplified it a bit too, with it renamed
_StructureTypes_ to _Structures_ and removed _isStructureType_). They
can be used with .types or .has(). _BuildableAttacks_.has() is used in
**RadialMenuElements**. _BuildableAttacks_ and existing _Structures_ now
make up _BuildMenus_. Which is used in **BuildMenu**,
**StructureIconsLayer** and **UnitDisplay**. Then _BuildMenus_ together
with UnitType.TransportShip make up the _PlayerBuildables_. Which is
used in **PlayerImpl** _buildableUnits_ (see point below). And with
_PlayerBuildableUnits_ we get the new _PlayerBuildableUnitType_ (see
above point on Game / PlayerImpl).

- **RadialMenuElements**: replace non-central ATTACK_UNIT_TYPES in
**RadialMenuElements** with centralized _BuildableAttackTypes_ too. Use
_PlayerBuildableUnitType_ for more type safety (can't by mistake add
UnitType.Train to its build menu). Make use of _BuildableAttackTypes_
instead of adding items hardcoded line by line in _getAllEnabledUnits_,
just like we already did since PR 3239 with _StructureTypes_. And use
_BuildableAttacks.types_ in the same fashion that existing
_isStructureTypes_ (now Structures.types) was already used elsewhere.

- **PlayerImpl**: _buildableUnits_ 
-- would do Object.values(UnitTypes) on every call. Now for better perf
directly loop over player buildable units by using _PlayerBuildables_
(see above point). In this way we also exclude MIRVWarhead, TradeShip,
Train, SamMissile and Shell so there are less unit types to loop through
by default. Since a player doesn't build those by themselves, they are
only build by Executions which use _canBuild_ directly and not
_buildableUnits_.
-- for more performance, do for loop instead of using .map and .filter,
no intermediate array needed nor callback overhead. We just loop over
the given units (which if undefined will contain _PlayerBuildables_).
Also pre-allocate the results array to get the most out of it, even if
V8 might already be very good at this.
-- cache config, railNetwork and inSpawnPhase so they can be re-used
inside the for loop.
-- cache cost inside the loop
-- it would check twice for tile!==null to decide to call
findUnitToUpgrade and canBuild. Now once.
-- eliminated double/triple checks for the same thing. It called
_findUnitToUpgrade_ (and with that _canUpgradeUnit_) and then _canBuild_
which both check if player has enough gold for the cost of the unit
type. And they both check if the unit type is disabled. Now we call
private functions _canBuildUnitType_, _canUpgradeUnitType_ to first do
checks on unit type level for early returns, and
_findExistingUnitToUpgrade_ to find existing unit without doing anything
extra. in a specific order to check everything only once. The public
functions _findUnitToUpgrade_ and _canBuild_ have an unchanged
functionality and we don't call them from _buildableUnits_ anymore.
-- would get _overlappingRailRoads_ and _computeGhostRailPaths_ when
canBuild was true. But this data is only meant for
**StructureIconsLayer** and it logically only uses it when placing a new
unit, not when upgrading one. Which is also commented on line 351 of
**StructureIconsLayer**. So, we now only get overlapping railroads and
ghost rails if we're not hovering to upgrade an existing unit.

- **PlayerImpl**: _findUnitToUpgrade_: unchanged functionality, but have
it call new private function _findExistingUnitToUpgrade_ to find
existing unit.

- **PlayerImpl**: _canBuild_: unchanged functionality, but have it call
new private function _canBuildUnitType_ to do the checks it first did
itself. And then new private function _canSpawnUnitType_ for the rest of
the checks. This way we can call _canBuildUnitType_ and
_canSpawnUnitType_ from _buildableUnits_ in a specific order to prevent
double/triple checks.

- **PlayerImpl**: _canBuildUnitType_: new private function to be shared
by _buildableUnits_, _canBuild_ and _canUpgradeUnit_ to be able do unit
type level checks in a specific order to prevent double/triple checks.
Via parameter knownCost, _buildableUnits_ can send it the cost it
already fetched so that it doesn't have to be fetched again. For caller
_canUpgradeUnit_, the isAlive() check (which was previously only done in
canBuild) is new but harmless, maybe even better to have also check
isAlive() on upgrade now that Nations are also upgrading which might
prevent some edge case bugs.

- **PlayerImpl**: _canUpgradeUnitType_: new private function to be
shared by _buildableUnits_ and _canUpgradeUnit_ to be able do unit type
level checks in a specific order to prevent double/triple checks.

- **PlayerImpl**: _canSpawnUnitType_: new private function to be shared
by _buildableUnits_ and _canBuildUnit_ to be able do unit type level
checks in a specific order to prevent double/triple checks.

- **PlayerImpl**: _findExistingUnitToUpgrade_: new private function to
be shared by _buildableUnits_ and _findUnitToUpgrade_ to be able do unit
level checks in a specific order to prevent double/triple checks.

- **PlayerImpl**: _isUnitValidToUpgrade_: new private function to be
shared by _buildableUnits_ and _canUpgradeUnit_ to be able do unit level
checks in a specific order to prevent double/triple checks.

- **PlayerImpl.test.ts**: because of the isAlive() check in which is new
for _canUpgradeUnit_ (see above at _canBuildUnitType_), the tests needed
to have the players be alive at the start, in order to pass.

- **BuildMenu**: use .find instead of .filter in canBuildOrUpgrade, a
function we already needed to change. This is faster and prevents an
allocation.


**PERFORMANCE**
As for calling ._buildables_ instead of unnecessarily getting
._actions_, there is an obvious win because there's less to send
calculate and recieve.

Also asking for only the needed buildings helps a lot (especially if
TradeShip isn't needed, see the difference in benchmark in original
#3193).

But the real-world impact is hard to measure. gave it a try in #3193 and
those results should be even better now.

Now testing only _buildableUnits_ performance in a synthetic benchmark,
we get these results. This is after other performance improvments so the
base is already better than it was in original #3193:

**BEFORE** (only buildableUnits itself)
<img width="602" height="96" alt="image"
src="https://github.com/user-attachments/assets/7770c0fa-a35e-42fc-90de-1de83242ec23"
/>

**AFTER** (only buildableUnits itself)
<img width="603" height="91" alt="image"
src="https://github.com/user-attachments/assets/a1578382-7010-4160-937c-7117bad18beb"
/>


## 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: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-03-04 11:32:45 -08:00
evanpelle 2a0b0f890d fix build 'cannot find RankedType' in ApiSchemas.ts 2026-03-03 19:14:19 -08:00
evanpelle 0976c02326 bugfix: add 'Basic' to the otel auth header 2026-03-03 18:57:44 -08:00
evanpelle c701ddbdcc Merge branch 'v29' 2026-03-03 18:46:00 -08:00
Evan 799da9d1b7 Migrate to a new prod machine: falk2 (#3346)
## Description:

Migrate to a beefier machine

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

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

evan
2026-03-03 18:45:24 -08:00
Evan d828ecfabf Add traefik with tls to startup script (#3343)
## Description:

We are migrating to traefik and away from CF tunnels due to reliability
issues. This PR sets up traefik with tls configured.

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

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

evan
2026-03-03 18:34:47 -08:00
evanpelle 8524afed65 Bugfix: Update PlayerGameSchema.map from GameMapType enum to string
Same issue as the users/@me response: a user played a map that doesn't exist on production
2026-03-03 17:45:51 -08:00
evanpelle 2139fbf1d8 bugfix: change SingleplayerMapAchievementSchema.mapName schema to be a string
A player got an achievement on the beta deployment for a map that doesn't exist in prod. Now they can't log in on prod because the map doesn't exist.
2026-03-03 17:45:09 -08:00
FloPinguin 0b9d43cb46 Configurable nation count 🤖 (#3338)
## Description:

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

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


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

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

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

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

### Warning

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

## Please complete the following:

- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [X] I have added relevant tests to the test directory
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

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

FloPinguin
2026-03-03 14:07:06 -08:00
evanpelle 40a7b49b89 Bugfix: Update PlayerGameSchema.map from GameMapType enum to string
Same issue as the users/@me response: a user played a map that doesn't exist on production
v0.29.19
2026-03-03 14:00:29 -08:00
evanpelle c77f579969 bugfix: change SingleplayerMapAchievementSchema.mapName schema to be a string
A player got an achievement on the beta deployment for a map that doesn't exist in prod. Now they can't log in on prod because the map doesn't exist.
2026-03-03 13:15:57 -08:00
scamiv 28bbd933a4 Revert "fix: resolve drawImage scaling penalty on non-square sprite height" (#3337)
Reverts openfrontio/OpenFrontIO#3320

doesnt do what it says

The #3320 description claimed it “resolves a performance parsing
penalty” and fixes “non-square sprite” scaling/ghosting issues.
In reality, the code change was limited to:

* **clearRect**: switched from clearing a `clearsize x clearsize` square
(`clearsize = sprite.width + 1`) around `lastX/lastY` to clearing a
**(sprite.width+2) x (sprite.height+2)** rect around **rounded**
`clearX/clearY` (with an extra 1px pad via `-1/+2`).
* **drawImage**: changed a single call’s destination height from
`sprite.width` → `sprite.height`.

### Why revert

For unit rendering, sprites are square, so the drawImage change is a
no-op in practice, and the main effect was **clearing more pixels per
frame**. Any theoretical gain from rounding coordinates is speculative,
and is outweighed by the increased clear area/overdraw.
2026-03-03 11:30:22 -08:00
VariableVince 1d1b076672 Rename/fix: change Bots to Tribes (#3290)
## Description:

Resolves #3285. As discussed on Discord.

However, in at least one instance "Tribes" feels a bit off: in Humans vs
Nations, team "Tribes" feels as human too while they are just bots.

This PR changes Bots to Tribes outwardly by 
- Changing default EN translation.
- Changing (untranslated) alt text in PlayerPanel.
- To change "Team Bot" into "Team Tribes" too in PlayerInfoOverlay and
TeamStats (team leaderboard in-game), translate team names in there from
now on too.
- This way we also fix a bug where team names were not translated yet in
there. To add to that fix, also translate team names in LobbyPlayerView
in the same way. For this we re-use the existing
getTranslatedPlayerTeamLabel function from GameLeftSideBar by moving it
to Utils.
- No translation key was present yet for Humans and Nations teams, so
added those to now be used in PlayerInfoOverlay, LobbyPlayerView and
TeamStats for completeness.
- No internal code changes so nothing breaks.

**BEFORE (showing old team name Bot and also that team names weren't
translated yet in TeamStats)**
![No translation yet in
TeamStats](https://github.com/user-attachments/assets/38f465bc-ef82-4474-806c-015bb640d233)

![No translation yet in TeamStats
2](https://github.com/user-attachments/assets/a4387f1e-0e80-491d-b57d-e52b3c616e2b)


**AFTER** (translations in Dutch only shown as proof here, did not
include nl.json in the PR)
![AFTER translated in TeamStats for Humans vs Nations as an example in
NL
json](https://github.com/user-attachments/assets/1a7dcf4e-4263-4d6b-a992-58cb08a4fa7b)
![AFTER Tribe as player type in
PlayerInfoPanel](https://github.com/user-attachments/assets/6fd09686-320e-4fee-9c0d-397e581aa676)
![AFTER translated Team name PlayerInfoPanel as an
example](https://github.com/user-attachments/assets/1b4bc684-9ef4-47a9-b91c-4ed5cda65e9e)
![AFTER Tribes in EN now that it is translated in TeamStats so fetched
from EN
json](https://github.com/user-attachments/assets/5ea6528b-7e3c-4c6e-abeb-2769fb0aedee)
![AFTER Instructions example of changed text
](https://github.com/user-attachments/assets/6c7a7ab7-1dea-4f11-bacf-3e2edcdb074b)



## 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-03-02 18:20:10 -08:00
Ryan 29e1ca2bda new homepage (#3332)
## Description:

updated homepage to make ffa the primary focus. closes
https://github.com/openfrontio/OpenFrontIO/issues/3288 and closes
https://github.com/openfrontio/OpenFrontIO/pull/3328
<img width="1911" height="924" alt="image"
src="https://github.com/user-attachments/assets/f81a3471-6a24-44a5-baf9-c2fdc5a3cbc3"
/>

<img width="416" height="846" alt="image"
src="https://github.com/user-attachments/assets/0456423b-4418-4719-9236-d12cb3aa1c37"
/>

## 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 18:01:49 -08:00
Ryan 17c0dde45f ui polish (#3333)
## Description:

- disabled dragging in many places, select skin, select flag, select
lang, and footer stuff
- removed shadow from flags in flag selector
- added bounce to the lang selector

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

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

w.o.n

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-03-02 18:00:34 -08:00
Skigim 60f69a6408 perf: remove O(n) StructureIconsLayer render lookups (#3305)
Begins work on #3207

## Description:

This PR is the first optimization slice for #3207: it removes O(n)
render lookups in `StructureIconsLayer` by replacing array-first render
state with a unit-id keyed map, and tightens hot-path execution to
reduce per-tick allocations.

### What changed
- Refactored render state from array-first to `rendersByUnitId:
Map<number, StructureRenderInfo>`.
- Replaced O(n) lookup/delete paths with O(1) `Map#get` / `Map#delete`.
- Replaced `seenUnits` object-identity tracking with `seenUnitIds:
Set<number>`.
- Removed `tick()` array/closure chain (`map(...).forEach(...)`) and
switched to index-based loop.
- Reduced ghost-path allocation pressure by reusing a layer-level `Set`
for connected ally IDs instead of allocating `filter` + `map` + `new
Set` per ghost query.
- Added dirty-flag caching for structure visibility focus
(`visibilityStateDirty`) so expensive visibility-state scans recompute
only when toggles change.

### Performance validation (before/after)
Benchmark added: `tests/perf/StructureIconsLayerLookupPerf.ts`

Command:
`npm run perf`

Observed result:
- `StructureIconsLayer BEFORE (array O(n) lookup/delete) x 0.33 ops/sec
±13.28%`
- `StructureIconsLayer AFTER (unit-id map O(1) lookup/delete) x 95.65
ops/sec ±2.46%`
- Fastest implementation: AFTER (unit-id map)

#### Profiler screenshots are too noisy to be useful for such a focused
change

### Verification
- `npx tsc --noEmit` 
- `npx eslint src/client/graphics/layers/StructureIconsLayer.ts
tests/perf/StructureIconsLayerLookupPerf.ts` 
- `npm run perf` 

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

skigim
2026-03-02 17:40:15 -08:00
bijx c238af389a Feat: Passage Map 🚢 (#3304)
## Description:
Introduces Passage, an island filled thin map (like Amazon River but
inverted) which is a long, thin stretch of islands that makes for some
really fun gameplay. I playtested with 3 friends a number of times and
it's great in pretty much all the modes. Naval gameplay on this map is
particularly fun since some islands in the center basically hold the
choke points on trade ships passing from one side to another, making
them hotly contested territory.

<img width="6000" height="400" alt="long map"
src="https://github.com/user-attachments/assets/7904d6f8-e7b8-437d-852e-68a2f006d200"
/>


Describe the PR.

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

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

bijx
2026-03-02 15:35:19 -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