Commit Graph

3848 Commits

Author SHA1 Message Date
evanpelle a2b3db616b WebGL: re-upload structures when owner changes (capture)
The structure dirty flag missed ownership changes, so captured buildings
kept rendering in the previous owner's color until another structure
event (level/active/construction) forced a re-upload.
2026-05-22 11:07:48 +01:00
VariableVince 2ee2fb97e3 WebGL: return of factory/defence post radii, and railroad highlighting when placing city/port right on top (#3981)
## Description:

Show factory and defence post radius for ghost structure when placing
structures from build bar (unitdisplay).

Show when city/port is placed directly over existing railroad, by
highlighting the railroad green. The railroad is not highlighted when
instead a city/port nearby the ghost structure will be upgraded instead
of placing it on the railroad. This works with the existing code in
buildableUnits in PlayerImpl: it would already return an empty array []
for overlappingRailroads and for ghostRailPaths when canUpgrade is
false. So the old checks for uiState for Canvas2D in
BuildPreviewController weren't even needed per se, they followed the
same logic as buildableUnits in PlayerImpl already did.

Both changes emulate how it worked before the move to WebGL. 

- OverlappingRailroads now returns TileRefs instead of a railroad ID,
and it does so with less allocations than the previous code. It's a
determistic outcome, sorted and deduplicated. In doubt about this a bit,
because it's better also in case we ever do desync checks using this
data, but for the rendering it isn't needed per se and could be more
performant without allocations.
- Also: Cleanup obsolete Canvas2D rail highlighting state (UIState) that
was superseded by GhostPreviewData.

## 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-05-22 10:33:09 +01:00
VariableVince ff11d6b8d1 WebGL: show status icons closer above names again and emoji on top (#3983)
## Description:

In the WebGL implementation, room was reserved for emoji icon between
the status icons and the name.. This is a regression from the HTML
NameLayer and it looks weird having that space sit there unused the
majority of the time when no emoji is shown.

Show status icons closer above names again. 

And when emoji icon is shown shortly, display it in place of the other
icons (with the HTML NameLayer it would be drawn on top of the other
icons but that could look messy and it's only for a short time anyway).

Not addressed in this PR: icon size is different from before WebGL
implementation, they seem smaller.

**BEFORE (after initial WebGL implementation):**
<img width="1007" height="577" alt="Icons too high up because room is
kept for emoji while on canvas they where stacked"
src="https://github.com/user-attachments/assets/ca6937c8-e265-467d-a8f5-1424540da1c1"
/>

**AFTER:**
<img width="816" height="538" alt="Icons closer and stacked again just
their size needs attention later on"
src="https://github.com/user-attachments/assets/8a700d23-ea82-4b61-b897-109dbd0e3a32"
/>

<img width="1878" height="758" alt="Icons closer and stacked again just
their size needs attention later on B"
src="https://github.com/user-attachments/assets/f6502ad1-8a6a-4dd8-80a1-7f0f2ed590a3"
/>

<img width="443" height="335" alt="Emoji replaces normal status icons
this was after emoji was just shown"
src="https://github.com/user-attachments/assets/a8b35385-08d1-4c00-9881-9607d880048e"
/>


## 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-05-22 10:31:06 +01:00
evanpelle 40c23ed5dc Raise railroad zoom thresholds so rails pop in later 2026-05-21 20:33:26 +01:00
VariableVince 7acf1aebc2 Restore on webgl context loss (#3968)
## Description:

When WebGL context is lost, restore context and all elements.

In GameView, handle potentially transient undefined states during
context loss gracefully.

Test with chrome://gpucrash from another tab, then return to the game
tab to see it being restored
(This fake gpucrash only works once sometimes. Because the second time
the browser might reject the tab it thinks caused the gpu crash, access
to hardware acceleration. And after even more tries even disables it
browser-wide. A browser restart resets it in that case.)

## 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-05-21 20:27:45 +01:00
Evan 41ef675e98 Improve Notification Panel (#3913)
Resolves #3910

## Description:

- Split the events HUD into two components: a new
**`<actionable-events>`** that owns alliance prompts (request / renew)
and a slimmed-down **`<events-display>`** for everything else.
- Reworked `<events-display>` into two visual tiers: dim/scrolling tier
2 on top (trade results, unit losses, donations, alliance status),
prominent tier 1 anchored at the bottom (inbound nukes, naval invasion,
attack requests, alliance broken, conquered player, chat). Tier 2 caps
at the 4 newest entries; events expire after 8s.
- Added a transient **+gold pip** above the gold pill in
`<control-panel>`, animated with a small fade-in. Fires for trade ships,
trains, donations, and conquest. Trade-ship and train arrivals are
removed from the events scroll since they're surfaced here instead.
- New `MessageType.NUKE_DETONATED` and a server-side emission in
`NukeExecution.detonate` — once an inbound nuke lands or gets
intercepted, the inbound warning vanishes and a "detonated" entry takes
its place.
- `displayMessage` gained optional `unitID` and `focusPlayerID` params
so events can link to a unit or a player. Unit captures and destructions
now navigate to the unit's last tile when clicked; donations navigate to
the other player.
- ActionableEvents card width matches `<events-display>`; cards persist
until the user clicks Accept/Reject/Renew/Ignore or the server-side
request timeout expires.
- Removed the in-events category filter UI and the gold-amount banner —
`<events-display>` is now a lightweight log that hides entirely when
empty.

<img width="570" height="444" alt="Screenshot 2026-05-21 at 1 42 30 PM"
src="https://github.com/user-attachments/assets/f103efb3-0e11-4b72-a11b-91ff6896177c"
/>

<img width="430" height="296" alt="Screenshot 2026-05-21 at 1 41 34 PM"
src="https://github.com/user-attachments/assets/ae58475a-b252-4aa6-9ce5-99dea7575ce3"
/>

## 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-05-21 19:50:10 +01:00
VariableVince 513057a62c WebGL: show alliance request+duration icon, show ally and team mate targets too, some optimization (#3971)
## Description:

Show nuke icons during replay too (when there's no localPlayer).
Show alliance request envelope icon, and duration in alliance icon
(weren't calculated yet).
Show ally and team mates' targets too (weren't calculated yet).

Remove unnecessary allocations. Nukes loop allocated two new sets,
transitive targets was a new set and now uses predicate with fallback to
localPlayer.targets, localPlayer.allies and localPlayer.embargoes were
both put in new set instead of using .includes 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:

tryout33
2026-05-19 18:17:26 -07:00
Evan c82b078dab Stagger territory tile rendering across frames (#3973)
relates to #893 

## Description:

Territory updates were uploaded in one shot per game tick, producing a
10 Hz tile update which looked choppy. This change drips each tick's
tile changes across the ~6 render frames between ticks so the fill flows
continuously instead of stepping.

Inside TerritoryPass, each changed tile is hashed by ref into one of N
buckets (configurable via tileDrip.bucketCount, set to 9 — gives ~50 ms
of jitter headroom over the tick period without making attacks feel
laggy). One bucket drains per render frame. The stable per-ref hash
keeps repeated updates to the same tile in arrival order, so the latest
owner always wins.

While in there, moved trail state ownership out of TerritoryPass and
into TrailPass where it belongs — the territory shader doesn't sample
trailTex, so the colocation was just code-reuse drift.

## 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-05-19 17:35:25 -07:00
RickD004 6a8b6a1b2f Changes for Rebalancing the Taiwan Strait map (#3970)
## Description:

Resizes the Taiwan Strait map for v32, to rebalance it for 2 team games:

Yesterday the map was tested in a stream and it turned out to be
unbalanced due to the size difference of the landmasses:
https://youtu.be/gildSwTdd4I?t=2516

Adds Team spawnzones for 2 teams.

Also removes and adds new nations , suggested by its map creator
crunchybbbbb_59469 on Discord

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

Taiwan Strait map by crunchybbbbb_59469 on Discord, who i worked with in
this rebalance

---------

Co-authored-by: FloPinguin <25036848+FloPinguin@users.noreply.github.com>
2026-05-19 21:40:07 +00:00
dependabot[bot] 0ace428a41 Bump the npm_and_yarn group across 1 directory with 3 updates (#3964)
Bumps the npm_and_yarn group with 3 updates in the / directory:
[protobufjs](https://github.com/protobufjs/protobuf.js),
[@opentelemetry/exporter-logs-otlp-http](https://github.com/open-telemetry/opentelemetry-js)
and
[@opentelemetry/exporter-metrics-otlp-http](https://github.com/open-telemetry/opentelemetry-js).

Removes `protobufjs`

Updates `@opentelemetry/exporter-logs-otlp-http` from 0.216.0 to 0.218.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/open-telemetry/opentelemetry-js/releases">@​opentelemetry/exporter-logs-otlp-http's
releases</a>.</em></p>
<blockquote>
<h2>experimental/v0.218.0</h2>
<h2>0.218.0</h2>
<h3>🚀 Features</h3>
<ul>
<li>feat(otlp-transformer): replace protobufjs metrics serialization
with custom implementation <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6629">#6625</a>
<a
href="https://github.com/pichlermarc"><code>@​pichlermarc</code></a></li>
<li>feat(configuration): show all config validation errors, if there are
multiple <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6683">#6683</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>feat(sdk-node): allow startNodeSDK() without an arg <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6688">#6688</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
</ul>
<h3>🏠 Internal</h3>
<ul>
<li>refactor(sdk-logs): alias <code>LoggerProviderConfig</code> to
<code>LoggerProviderOptions</code> <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6691">#6691</a>
<a
href="https://github.com/david-luna"><code>@​david-luna</code></a></li>
<li>refactor(sdk-logs): use <code>Logger.enabled()</code> within
<code>Logger.emit()</code> implementation <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6680">#6680</a>
<a
href="https://github.com/david-luna"><code>@​david-luna</code></a></li>
</ul>
<h2>experimental/v0.217.0</h2>
<h2>0.217.0</h2>
<h3>🚀 Features</h3>
<ul>
<li>feat(otlp-transformer): replace protobufjs trace serialization with
custom implementation <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6625">#6625</a>
<a
href="https://github.com/pichlermarc"><code>@​pichlermarc</code></a></li>
<li>feat(configuration): auto-generate TypeScript types from OTel
declarative config JSON schema (stable v1.0.0) using
<code>json-schema-to-typescript</code> and <code>ajv</code> <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6533">#6533</a>
<a
href="https://github.com/MikeGoldsmith"><code>@​MikeGoldsmith</code></a></li>
<li>feat(configuration, sdk-node): <code>startNodeSDK()</code> code path
now uses <code>log_level</code> configuration to setup a
DiagConsoleLogger <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6668">#6668</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a>
<ul>
<li>Note that allowed values for <code>log_level</code> in a
configuration YAML file are <em>not</em> the same set as for
<code>OTEL_LOG_LEVEL</code>. Use <code>log_level: trace</code> to see
<em>all</em> logs (equivalent of <code>OTEL_LOG_LEVEL=ALL</code>). Use
<code>log_level: fatal</code> to effectively disable the SDK's internal
diagnostic logger (equivalent of <code>OTEL_LOG_LEVEL=NONE</code>).</li>
<li>If <code>log_level</code> is not specified, a diagnostic console
logger at &quot;info&quot; level will be setup.</li>
<li>An invalid YAML config file will now result in a noop OTel SDK.</li>
</ul>
</li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>fix(configuration): do not validate <code>OTEL_CONFIG_FILE</code>
value before using it for file config <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6643">#6643</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>fix(configuration): improve how 'additionalProperties' in JSON
schema is translated to TS types <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6650">#6650</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>fix(configuration): remove stripMinItems and preprocessNullArrays
from validation/parsing <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6657">#6657</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>fix(configuration): improve handling of enums in generated types <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6659">#6659</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>fix(configuration): improve the technique for removing '| null' on
types the JSON Schema <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6662">#6662</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>fix(sampler-jaeger-remote): add missing axios dep <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6656">#6656</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>fix(exporter-prometheus): handle malformed URLs in Prometheus
exporter request handler <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6674">#6674</a>
<a href="https://github.com/homanp"><code>@​homanp</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/06ad0eaaecbd49f5ead871325f852cc2a3454079"><code>06ad0ea</code></a>
chore: prepare next release (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6703">#6703</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/38ca257e64ebd13f5603d5539f8a48d6d9232037"><code>38ca257</code></a>
feat(otlp-transformer): replace protobufjs metrics serialization with
custom ...</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/013c60085b84351a4c1e4e4f79e3dd67c56661cd"><code>013c600</code></a>
chore: prepare next release (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6699">#6699</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/b7a0c63bde39d7916fdb73cbb3d64cf1c93282c5"><code>b7a0c63</code></a>
feat(semantic-conventions): update semantic conventions to v1.41.1 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6695">#6695</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/774143b2c6667c6537c000ab48ea5ce998278ca0"><code>774143b</code></a>
chore(renovate): add minimumReleaseAge to config (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6697">#6697</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/e0dafe0d9fadaccf7dd8d7b02dd85531356e2ac1"><code>e0dafe0</code></a>
fix(otlp-exporter-base): remove brackets from IPv6 hostname in HTTP
transport...</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/f804c93d1d6d903036b8bf38f8c3713dbbaf0360"><code>f804c93</code></a>
chore(deps): update github/codeql-action digest to 68bde55 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6682">#6682</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/95e48e7afcc475ded350b95b43070c54591ecbbb"><code>95e48e7</code></a>
refactor(sdk-logs): alias <code>LoggerProviderConfig</code> to
<code>LoggerProviderOptions</code> (...</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/907b627d9ada25844b0f06551ecd9bbda5c0ea4f"><code>907b627</code></a>
feat(sdk-node): allow startNodeSDK() without an arg (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6688">#6688</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/0d1526198fafe7f90078ff353103d0427e6c64d4"><code>0d15261</code></a>
docs: Add SIG meeting info and welcoming language (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6689">#6689</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/open-telemetry/opentelemetry-js/compare/experimental/v0.216.0...experimental/v0.218.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `@opentelemetry/exporter-metrics-otlp-http` from 0.216.0 to
0.218.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/open-telemetry/opentelemetry-js/releases">@​opentelemetry/exporter-metrics-otlp-http's
releases</a>.</em></p>
<blockquote>
<h2>experimental/v0.218.0</h2>
<h2>0.218.0</h2>
<h3>🚀 Features</h3>
<ul>
<li>feat(otlp-transformer): replace protobufjs metrics serialization
with custom implementation <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6629">#6625</a>
<a
href="https://github.com/pichlermarc"><code>@​pichlermarc</code></a></li>
<li>feat(configuration): show all config validation errors, if there are
multiple <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6683">#6683</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>feat(sdk-node): allow startNodeSDK() without an arg <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6688">#6688</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
</ul>
<h3>🏠 Internal</h3>
<ul>
<li>refactor(sdk-logs): alias <code>LoggerProviderConfig</code> to
<code>LoggerProviderOptions</code> <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6691">#6691</a>
<a
href="https://github.com/david-luna"><code>@​david-luna</code></a></li>
<li>refactor(sdk-logs): use <code>Logger.enabled()</code> within
<code>Logger.emit()</code> implementation <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6680">#6680</a>
<a
href="https://github.com/david-luna"><code>@​david-luna</code></a></li>
</ul>
<h2>experimental/v0.217.0</h2>
<h2>0.217.0</h2>
<h3>🚀 Features</h3>
<ul>
<li>feat(otlp-transformer): replace protobufjs trace serialization with
custom implementation <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6625">#6625</a>
<a
href="https://github.com/pichlermarc"><code>@​pichlermarc</code></a></li>
<li>feat(configuration): auto-generate TypeScript types from OTel
declarative config JSON schema (stable v1.0.0) using
<code>json-schema-to-typescript</code> and <code>ajv</code> <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6533">#6533</a>
<a
href="https://github.com/MikeGoldsmith"><code>@​MikeGoldsmith</code></a></li>
<li>feat(configuration, sdk-node): <code>startNodeSDK()</code> code path
now uses <code>log_level</code> configuration to setup a
DiagConsoleLogger <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6668">#6668</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a>
<ul>
<li>Note that allowed values for <code>log_level</code> in a
configuration YAML file are <em>not</em> the same set as for
<code>OTEL_LOG_LEVEL</code>. Use <code>log_level: trace</code> to see
<em>all</em> logs (equivalent of <code>OTEL_LOG_LEVEL=ALL</code>). Use
<code>log_level: fatal</code> to effectively disable the SDK's internal
diagnostic logger (equivalent of <code>OTEL_LOG_LEVEL=NONE</code>).</li>
<li>If <code>log_level</code> is not specified, a diagnostic console
logger at &quot;info&quot; level will be setup.</li>
<li>An invalid YAML config file will now result in a noop OTel SDK.</li>
</ul>
</li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>fix(configuration): do not validate <code>OTEL_CONFIG_FILE</code>
value before using it for file config <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6643">#6643</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>fix(configuration): improve how 'additionalProperties' in JSON
schema is translated to TS types <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6650">#6650</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>fix(configuration): remove stripMinItems and preprocessNullArrays
from validation/parsing <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6657">#6657</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>fix(configuration): improve handling of enums in generated types <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6659">#6659</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>fix(configuration): improve the technique for removing '| null' on
types the JSON Schema <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6662">#6662</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>fix(sampler-jaeger-remote): add missing axios dep <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6656">#6656</a>
<a href="https://github.com/trentm"><code>@​trentm</code></a></li>
<li>fix(exporter-prometheus): handle malformed URLs in Prometheus
exporter request handler <a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6674">#6674</a>
<a href="https://github.com/homanp"><code>@​homanp</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/06ad0eaaecbd49f5ead871325f852cc2a3454079"><code>06ad0ea</code></a>
chore: prepare next release (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6703">#6703</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/38ca257e64ebd13f5603d5539f8a48d6d9232037"><code>38ca257</code></a>
feat(otlp-transformer): replace protobufjs metrics serialization with
custom ...</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/013c60085b84351a4c1e4e4f79e3dd67c56661cd"><code>013c600</code></a>
chore: prepare next release (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6699">#6699</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/b7a0c63bde39d7916fdb73cbb3d64cf1c93282c5"><code>b7a0c63</code></a>
feat(semantic-conventions): update semantic conventions to v1.41.1 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6695">#6695</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/774143b2c6667c6537c000ab48ea5ce998278ca0"><code>774143b</code></a>
chore(renovate): add minimumReleaseAge to config (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6697">#6697</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/e0dafe0d9fadaccf7dd8d7b02dd85531356e2ac1"><code>e0dafe0</code></a>
fix(otlp-exporter-base): remove brackets from IPv6 hostname in HTTP
transport...</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/f804c93d1d6d903036b8bf38f8c3713dbbaf0360"><code>f804c93</code></a>
chore(deps): update github/codeql-action digest to 68bde55 (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6682">#6682</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/95e48e7afcc475ded350b95b43070c54591ecbbb"><code>95e48e7</code></a>
refactor(sdk-logs): alias <code>LoggerProviderConfig</code> to
<code>LoggerProviderOptions</code> (...</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/907b627d9ada25844b0f06551ecd9bbda5c0ea4f"><code>907b627</code></a>
feat(sdk-node): allow startNodeSDK() without an arg (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6688">#6688</a>)</li>
<li><a
href="https://github.com/open-telemetry/opentelemetry-js/commit/0d1526198fafe7f90078ff353103d0427e6c64d4"><code>0d15261</code></a>
docs: Add SIG meeting info and welcoming language (<a
href="https://redirect.github.com/open-telemetry/opentelemetry-js/issues/6689">#6689</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/open-telemetry/opentelemetry-js/compare/experimental/v0.216.0...experimental/v0.218.0">compare
view</a></li>
</ul>
</details>
<br />


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-05-18 19:43:28 -07:00
RickD004 6591b055c3 Adds map of Venice 🛶 (#3935)
## Description:

Adds map of Venice. A relatively small map (similar land area to World)
for heavy trade and lots of boating.

Because of the very low difference of elevation of the zone, terrain is
instead used to show buildings.

Map source from OpenStreetMap, already credited in CREDITS.md

Very requested map, with 2 discord posts suggesting it with +15 upvotes
each

<img width="794" height="569" alt="image"
src="https://github.com/user-attachments/assets/ca7d44f2-cfc9-4e93-b7d4-43dbe62f74d4"
/>

## 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-05-18 19:43:02 -07:00
Ryan 15ac42b4c1 streamer mode bugfix (#3953)
## Description:

fixes 
https://github.com/openfrontio/OpenFrontIO/issues/3572

streamer mode bufix


## 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-05-18 19:20:29 -07:00
VariableVince a2aa7823a4 Display player flags next to their names again (#3965)
## Description:

Display flags again.

## 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-05-18 19:19:02 -07:00
evanpelle 17e3ac4b05 make spawn glow follow the player's currently selected spawn tile
Plumb spawnTile through PlayerUpdate / PlayerState / applyStateUpdate
so the WebGL spawn overlay can read it directly. The glow was reading
nameData.x/y (territory centroid for label placement) which only
recomputes every 2 ticks and only when largestClusterBoundingBox has
been updated by PlayerExecution — both lag the player's actual spawn
click. Using spawnTile updates the same tick setSpawnTile() fires.

Also adds spawnTile to diffPlayerUpdate / applyStateUpdate so changes
after the initial full snapshot actually propagate (the recent
diff-only PlayerUpdate path silently dropped any field not enumerated
in those helpers).
2026-05-18 19:15:01 -07:00
evanpelle 0eb8578996 Fix nations not spawning in singleplayer when player picks fast
NationExecution gated its first SpawnExecution by the same
attackRate/attackTick throttle used for AI actions, so a nation
could wait up to ~100 ticks before scheduling its spawn. In
singleplayer the human's spawn ends the spawn phase immediately,
stranding any nation that hadn't yet reached its attackTick — on
the next tick its NationExecution sees inSpawnPhase()=false and
isAlive()=false (no tiles), and deactivates itself.

First spawn now fires on tick 1, gated by a one-shot flag to
avoid queuing duplicates. The attackRate cadence is preserved for
subsequent re-spawns so nations still hop locations during the
spawn phase.
2026-05-18 17:39:27 -07:00
Evan 62e15d2794 Cut worker→main bandwidth ~3.3× by switching PlayerUpdate to deltas (#3967)
## Description:

Cut worker→main bandwidth ~3.3× by switching PlayerUpdate from a full
per-tick snapshot to a field-level diff. PlayerImpl.toUpdate() now
caches the last sent update and returns only changed fields, or null if
nothing changed. The client-side applyStateUpdate() merges instead of
overwriting.

Per-tick total dropped from ~297 KB to ~89 KB; the Player bucket alone
went from 258 KB/tick to 50 KB/tick. Diff/apply logic lives in a new
GameUpdateUtils.ts module with unit tests.

## 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-05-18 17:07:40 -07:00
VariableVince ed928db081 Display territory skins again (#3966)
## Description:

Display territory skins (patterns) again.

## 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-05-18 15:48:05 -07:00
evanpelle 7863529b2c rename client/graphics → client/hud
The contents (Lit web components for in-game chat, build menu, leaderboard,
attack displays, etc.) are HUD, not graphics — the actual graphics is in
client/render/.
2026-05-18 13:07:26 -07:00
evanpelle f23789883b Merge webgl2 — full WebGL2 renderer migration
relates to #893

Replaces the canvas2D + Pixi.js map renderer with a pure WebGL2 pipeline.
Map-space visuals (terrain, names, structures, units, FX, selection
boxes, build ghosts, status icons, nuke trajectories, defense zones,
spawn glow, water-nuke terrain deltas) all render through dedicated
passes in src/client/render/gl/passes/. Controllers in
src/client/controllers/ push state directly to the WebGL view; no
relay events. Assets unified under resources/ + assetUrl(). Mode
toggle wired to the existing darkMode UserSetting (no more day/night
cycle). One input system (InputHandler + EventBus + TransformHandler).

Known regressions to address in follow-up work:

- [ ] webgl: highlight structures when hover on build menu
- [ ] webgl: custom flags, flag atlas
- [ ] webgl: territory patterns
- [ ] webgl: defense post outline
- [ ] webgl: territory expanse smoothing
2026-05-18 12:09:11 -07:00
evanpelle 1dd00f6264 push terrain deltas to the WebGL view so water nukes show
Terrain was uploaded once at game start and treated as static — water
nukes (land → water conversion) mutated the sim's terrain bytes but
the rendered terrain stayed dry.

Plumbed a delta path: TerrainPass and RailroadPass each get
applyTerrainDelta(refs, bytes), Renderer + GameView forward, and
WebGLFrameBuilder pushes each tick from gameView.recentlyUpdatedTerrainTiles().
Per-tile encoding is shared via the new encodeTerrainTile helper in
ColorUtils so the startup full-map build and the per-tile delta updates
can't drift.
2026-05-18 11:08:09 -07:00
evanpelle f7dabe6a98 add CLAUDE.md describing the WebGL renderer architecture 2026-05-18 10:07:19 -07:00
evanpelle 4936ae3d59 restore spawn-phase glow with a true breathing animation
SpawnOverlayPass had everything wired except a caller. WebGLFrameBuilder
now collects spawned human players each tick during spawn phase and
pushes their territory centroid + color through view.updateSpawnOverlay.
myPlayer reads as white so the local-player ring stands out.

Reshaped the shader animation: dropped the growing-disc effect, gave
the ring a true breath — radius scales 0.5×→1.15× while opacity pulses
35%→100% in phase. Replaced the sharp inner-edge ramp with a smooth
center-to-boundary fill so there's no hard cutoff or empty hole in
the middle. animSpeed bumped to 0.0035 (~1 breath/sec).
2026-05-18 09:43:14 -07:00
evanpelle 61f6d2fdd4 restore alt-view (space hold) toggle
InputHandler still emits AlternateViewEvent on space down/up, and the
renderer still has setAltView. The bridge between them lived in
MapInteraction's applyAltView, which got deleted with the rest of
MapInteraction — nothing was wiring the event to the view anymore.
Expose view.setAltView and have ClientGameRunner subscribe.
2026-05-18 09:10:05 -07:00
evanpelle 4cd22a9b5c rename render/ files to UpperCamelCase to match client convention
The render/ tree was the only place in the client still using kebab-case
filenames. Brings ~80 files in line with the rest of src/client/
(BuildPreviewController, TransformHandler, etc.). Directories kept as
they were (name-pass/, fx-pass/, passes/, utils/, debug/) since the
codebase already mixes those.

Two collisions surfaced and got resolved: render/types/ is a directory,
not a file, so its imports kept the lowercase form; and the sed pass
incidentally normalized core/pathfinding imports, which had to be
reverted since that file is actually lowercase on disk despite some
imports having referenced it as ./Types under macOS case-insensitive
resolution.
2026-05-17 21:21:05 -07:00
evanpelle 5a9694e2bd replace MapInteraction with HoverHighlightController; one input system
MapInteraction bound DOM events to the WebGL canvas, but the canvas has
pointer-events: none post-migration so its pointermove/down/up/wheel/
keydown listeners never fired — duplicating InputHandler (which owns
the inputOverlay div + EventBus pipeline) and leaving most features
dead. The one alive bit was hover→setHighlightOwner, which I'd
manually forwarded as a workaround.

Now there's a HoverHighlightController that listens to MouseMoveEvent,
computes the cursor's tile owner, and pushes setHighlightOwner. Delete
map-interaction.ts (418 lines) + keyboard-pan.ts, trim the DOM-binding
constructor + proxy methods (setFitZoomOnDoubleClick, setPanSpeed,
setZoomSpeed, etc.) out of GameView, and drop the ClientGameRunner
pointermove forwarder.

Input flows through one path: DOM → inputOverlay → InputHandler →
EventBus → controllers/renderer.
2026-05-17 20:46:02 -07:00
evanpelle fb45c27d82 add subtle player-tile highlight on nation hover
The hover wiring already pushed setHighlightOwner into the border pass,
but the WebGL canvas has pointer-events: none (post-migration to the
inputOverlay div) so MapInteraction's pointermove listener never fired.
Forward pointermove from the input overlay to view.handlePointerMove
so hover actually triggers.

While there, brighten every tile owned by the hovered player — the
territory frag shader now reads uHighlightOwner / uHighlightBrighten
and mixes toward white when the tile owner matches. Wired through
territory-pass.ts; renderer.setHighlightOwner forwards to both border
and territory passes. New highlightFillBrighten setting (0.15) keeps
the fill tint tunable independently of the existing highlightBrighten
border setting, which is dropped from 0.6 → 0.25 so neither effect
blows out.
2026-05-17 20:35:22 -07:00
evanpelle c197f5864f replace day/night cycle with a binary light/dark mode tied to UserSettings
The cycling sun/moon animation was distracting and not a fan favorite.
Drops the cycle path entirely — RenderSettings.dayNight.mode is now
"light" | "dark", and the cycle-only fields (cycleTicks, startPhase,
noonHold, nightHold) plus the passEnabled.dayNight toggle are gone.
getAmbient is a one-liner. The in-game mode follows the existing
darkMode UserSetting (same one that drives the page-level CSS class);
ClientGameRunner applies it on startup and on the per-key change event.
2026-05-17 20:01:23 -07:00
evanpelle 3eedaf7bbc wire nuke trajectory + blast radius into the build ghost preview
NukeTrajectoryPass and the rangeRadius pipe existed but had no caller —
trajectory arc and outer-blast circle never appeared during build mode.
BuildPreviewController now picks the closest active player silo as the
launch source, collects non-allied SAMs as threats, and pushes a
NukeTrajectoryData each preview tick. rangeRadius is set to
nukeMagnitudes(type).outer for AtomBomb / HydrogenBomb so the existing
RangeCirclePass renders the blast radius at the target.
2026-05-17 19:37:07 -07:00
evanpelle 4dc4810bcc render build ghost at cursor with sub-tile precision
Bypass the snap-to-tile in TransformHandler by adding
screenToWorldCoordinatesFloat. Each render frame, BuildPreviewController
re-emits the ghost preview at the cursor's exact world position
(adjusted by -0.5 to cancel the shader's tile-center offset). Buildable
validation still runs on the snapped tile at the 50ms throttle, but the
icon now follows the cursor 1:1 instead of stepping tile-to-tile.
2026-05-17 19:13:29 -07:00
evanpelle b8d72d3a4e set crossOrigin on WebGL atlas image loaders
Atlases now load from the CDN; without crossOrigin = "anonymous" the
browser refuses to texImage2D the cross-origin image. Requires the CDN
to send Access-Control-Allow-Origin for /_assets/atlases/.
2026-05-17 14:03:58 -07:00
evanpelle 69b5a9cba2 restore FPS tracking via self-driven RAF in PerformanceOverlay
updateFrameMetrics had zero callers — the canvas2D RAF loop used to
invoke it per-frame, and that loop died with canvas2D. Tick metrics
were unaffected since GameRenderer.tick() still calls
updateTickLayerMetrics directly.

The WebGL renderer doesn't expose a per-frame hook for the overlay, so
the overlay now drives its own RAF, started/stopped with visibility so
it stays off the hot path when hidden.
2026-05-17 13:48:35 -07:00
evanpelle b27c2984fd include atlases/ in the public asset manifest
resources/atlases/ wasn't in the manifest glob list, so the build
skipped hashing/copying it into static/_assets/ and the deploy
pipeline's R2 uploader had no keys for it — atlases 404'd on staging.
2026-05-17 13:08:08 -07:00
evanpelle be182bb7f7 delete dead canvas2D FX system
graphics/fx/ (6 files) and the AnimatedSprite/AnimatedSpriteLoader pair
were the canvas2D-era visual-effects pipeline. WebGL has its own FX
stack now (render/gl/passes/fx-pass/), so nothing outside the dead
cluster imported any of these. The only "reference" left was a stale
comment in fx-sprite-pass.ts.
2026-05-17 13:02:00 -07:00
evanpelle 8a4b12c4d6 move WebGL atlases into resources/atlases/, route through assetUrl()
src/client/render/gl/assets/ held 11 atlas files (PNGs + JSON metadata)
that bypassed the asset-manifest pipeline — they were imported via
Vite's ?url query, bundled, and served same-origin instead of going
through the CDN like every other game asset. Moved them to
resources/atlases/, switched the PNG imports to assetUrl("atlases/...")
so they flow through the manifest, and updated the JSON metadata
imports to "resources/atlases/..." paths. Also dropped an orphan copy
of MissileSiloIconWhite.svg (no callers; resources/images/ already had
the canonical version).

render-settings.json stays in src/ — it's renderer tuning config
consumed at bundle time, not a URL-served asset.
2026-05-17 12:54:57 -07:00
evanpelle a743a31897 delete dead canvas2D utilities, rename mountWebGLDebugRenderer → mountWebGLFrameLoop
ProgressBar and StructureDrawingUtils had no production callers — only
their own test referenced ProgressBar, and StructureDrawingUtils was a
canvas2D-era helper module that nothing imports anymore.

mountWebGLDebugRenderer was named back when WebGL was a side-by-side
debug overlay; it's the only renderer now, so the "Debug" prefix is
misleading. Also dropped the `\` keybind that hid the GL canvas — with
no other renderer, hiding it just blanks the game.
2026-05-17 12:31:57 -07:00
evanpelle eb046e5a58 move TransformHandler/UIState/Controller out of graphics/, drop dead GhostStructureChangedEvent
graphics/ was a canvas2D-era directory name — TransformHandler, UIState,
and the Controller interface aren't graphics, they're cross-cutting
client state. Hoist them to src/client/ so the path matches what they
are. GhostStructureChangedEvent had three emitters and zero listeners;
removed.
2026-05-17 12:24:41 -07:00
evanpelle 7b1557b886 controllers push to the WebGL view directly, drop ClientGameRunner relays
BuildPreviewController and WarshipSelectionController now take the WebGL
view in their constructor and call view.updateGhostPreview /
view.setSelectedUnits themselves instead of emitting bus events that
ClientGameRunner forwarded. Splits the old mountWebGLDebugRenderer in
two — createWebGLView builds the view up front so the renderer can wire
controllers to it, mountWebGLDebugRenderer does the per-frame plumbing
after the transformHandler exists. GhostPreviewUpdatedEvent had no
remaining consumers and is removed.
2026-05-16 22:58:31 -07:00
evanpelle a708a8c984 rename UILayer/StructureIconsLayer to controllers, move to src/client/controllers/
UILayer → WarshipSelectionController and StructureIconsLayer →
BuildPreviewController. These are the two real Controller implementations
(state + click handling, no rendering) — the new names + location reflect
what they actually do now that all rendering lives in WebGL passes.
2026-05-16 22:45:02 -07:00
evanpelle bac29448c2 rename Layer → Controller; drop canvas2D-era interface hooks
The Layer interface dates to the canvas2D era when each entry drew to
the shared 2D context via renderLayer(ctx). With canvas2D gone, nothing
draws there and the renderLayer hook is dead. Rename the interface
("main-thread analog of the worker's Execution") and trim it:

  interface Controller {
    init?: () => void;
    tick?: () => void;
    getTickIntervalMs?: () => number;
  }

renderLayer / shouldTransform / redraw are gone.

Sweep across 28 files: from "./Layer" → "./Controller", implements
Layer → implements Controller, Layer[] / Map<Layer,…> →
Controller[] / Map<Controller,…>. Delete the no-op renderLayer +
shouldTransform method bodies that every layer had inherited.

GameRenderer drops the RedrawGraphicsEvent listener + redraw() fanout
(nothing implements redraw anymore) and the now-unused eventBus
constructor field.

One real case: AttackingTroopsOverlay.renderLayer wasn't a no-op — it
updates DOM label transforms each frame so labels track the WebGL
camera during pan/zoom. Rename to private updateLabelDOM() and start
a self-driven RAF in init() so the per-frame updates keep running.

Class names ending in "Layer" (UILayer, StructureIconsLayer, NameLayer,
etc.) intentionally left as-is — those are separate identifiers and
the class-rename / file-move is a follow-up.

407 tests pass.
2026-05-16 22:35:14 -07:00
evanpelle b2f84aad33 delete canvas2D map canvas — WebGL is the only renderer left
After every map-anchored visual moved to WebGL (terrain, territory,
structures, names, selection boxes, ghost preview, move chevrons,
FX) there's nothing drawing to the canvas2D context. Rip it out.
2026-05-16 22:21:17 -07:00
evanpelle 5002dfdc2a delete Pixi build-ghost rendering from StructureIconsLayer
The WebGL ghost (StructurePass + RangeCirclePass + RailroadPass +
CrosshairPass) is fed via GhostPreviewUpdatedEvent and looks correct;
the Pixi-rendered ghost was a duplicate.

Strip the Pixi side out of StructureIconsLayer:
- delete imports for pixi.js / pixi-filters / colord plugins / Theme /
  SpriteFactory / StructureDrawingUtils / bitmapFont / renderNumber
- delete fields: pixicanvas, ghostStage, rootStage, renderer,
  rendererInitialized, theme, factory, filterRedArray, rebuildPending,
  and the Pixi half of ghostUnit (container, priceText, priceBg,
  priceGroup, priceBox, range, rangeLevel, targetingAlly) — now just
  { buildableUnit }
- delete methods: setupRenderer, redraw, rendererOrGLContextLost,
  resizeCanvas, renderLayer, shouldTransform, updateGhostPrice,
  updateGhostRange, plus the Pixi guts of moveGhost /
  createGhostStructure / clearGhostStructure

init() is now sync; the per-RAF state checks move to a tick()-driven
syncGhostState() + renderGhost() (renderGhost still re-queries
buildables, just no longer paints anything).

Net: ~250 LOC gone, no canvas2D drawing left in the layer. The
canvas2D map canvas itself has no remaining writers — ready to be
deleted in a follow-up.
2026-05-16 20:28:59 -07:00
evanpelle 923cba8c2d move multi-unit warship selection box to WebGL SelectionBoxPass
SelectionBoxPass now stores an array of selections and renders one
quad per entry. GPURenderer gains setSelectedUnits(ids) — the
single-unit setSelectedUnit becomes a wrapper. Position + color are
rebuilt each frame from lastUnits; dead unit IDs get pruned in place.

ClientGameRunner's UnitSelectionEvent listener forwards both single
and multi to view.setSelectedUnits — no more single/multi split.

UILayer drops everything canvas2D-related: the offscreen canvas +
context, theme, selectionAnimTime, multiSelectionBoxCenters,
SELECTION_BOX_SIZE, drawSelectionBoxMulti, paintSelectionBoxAt,
clearSelectionBox, paintCell, clearCell, and renderLayer / redraw /
shouldTransform. tick() now only prunes destroyed warships from the
selection list; the layer is purely state + click handling. ~120 LOC
gone.

Tests: UILayer.test.ts updated — drops the canvas/redraw asserts,
adds a multi-selection state assertion.
2026-05-16 20:02:31 -07:00
evanpelle ede0fb7668 move single-unit warship selection box to WebGL SelectionBoxPass
UnitSelectionEvent now forwards to view.setSelectedUnit(unit.id()) in
mountWebGLDebugRenderer; the renderer's SelectionBoxPass draws the
animated stippled outline on the GPU. UILayer still tracks
selectedUnit for game-logic readers (the click handlers) but no longer
paints to canvas2D for it.

Drops drawSelectionBox + lastSelectionBoxCenter (~50 LOC) plus the
per-tick single-unit redraw in tick(). Multi-selection stays on
canvas2D — SelectionBoxPass is single-unit only.

Test update: replaces the now-dead drawSelectionBox spy with a
selectedUnit state assertion + a deselect case.
2026-05-16 19:53:13 -07:00
evanpelle d1651017ea migrate warship drag rectangle from canvas2D to DOM overlay
The shift+drag warship selection rectangle was drawn on a second
offscreen canvas, blitted onto the main canvas2D context every frame
via world-coord transform. It's a screen-space rectangle though, so
none of that math was load-bearing.

Replace with a `<div>` positioned via inline left/top/width/height in
screen pixels. Same color tinting (player territoryColor lightened
0.2, dashed border at 0.85 alpha, fill at 0.06). pointer-events:none
so it doesn't intercept the drag.

Drops ~95 LOC of canvas2D drawing (renderSelectionBox, drawDashedLine,
selectionBoxCanvas/Ctx, the redraw() init, the renderLayer() blit).
One step closer to retiring the canvas2D map canvas — UILayer's
per-unit selection outlines are the last canvas2D draws on it.
2026-05-16 19:37:55 -07:00
Berk 2e17fb5184 fix: remove double x() dereference in MIRV separation point calc (#3940)
mg.x() takes a TileRef and returns a number (x coordinate). The code was
calling mg.x(mg.x(tile)), feeding the numeric result back into x() which
expects a TileRef. This produces an incorrect midpoint for MIRV warhead
separation, causing warheads to spread from a wrong position on the map.

If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves #(issue number)

## Description:

Describe the PR.

## 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-05-16 19:37:37 -07:00
Berk 5fefc21cb8 security: remove duplicate express.json() middleware (SEC-04) (#3947)
## Description:

The app had `express.json()` registered twice in `app.ts`. This can
cause issues with body parsing and is redundant.

**Fix:** Removed the second call to `app.use(express.json())`.

## Please complete the following:

- [x] I have added screenshots for all UI updates (N/A - no UI changes)
- [x] I process any text displayed to the user through translateText()
(N/A)
- [x] I have added relevant tests to the test directory (N/A - existing
tests pass)
- [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:

barfires
2026-05-16 19:33:47 -07:00
evanpelle 45246f2085 make computePlayerStatus live-aware so status icons render
The replay-path computePlayerStatus left alliance/target/embargo/
nukeTargetsMe at false, which meant the WebGL NamePass had no data
for those status icons after we switched names off canvas2D — they
just stopped appearing.

Add an opts param taking localPlayerID + tileState. When localPlayerID
is set, fill the relative flags by checking the local player's
allies/targets/embargoes against each other player's smallID;
embargo is bilateral (either side). nukeTargetsMe walks active nukes
and checks their targetTile's owner via the tileState buffer.

Plumb localPlayerID = myPlayer?.smallID() and tileState from
populateFrame so the live path uses the new mode. Emit an entry when
only a relative flag is true (previously could be dropped if no base
flag was set).

allianceReq and allianceFraction stay deferred (need local PlayerID
string for outgoing requests and current tick for fraction).

18 new tests covering both modes — replay (relative flags forced
false), and live (alliance one-way, target one-way, embargo bilateral,
self-flags suppressed, nukeTargetsMe with/without tileState,
relative-flag-alone emits, localPlayerID=0 falls back to replay,
allianceReq/allianceFraction stay deferred).
2026-05-16 19:21:49 -07:00
evanpelle 3481beba8a delete canvas2D NameLayer; render names via WebGL NamePass
Stop drawing names on canvas2D — NamePass already gets the placement
data (gameView.frameData().names) and lerps positions in-shader. Drop
the runtime passEnabled.name=false override in ClientGameRunner,
remove NameLayer from the layers list, and delete NameLayer.ts.

Known gaps (deferred):
- Player-uploaded flags not in the bundled atlas render as no-flag;
  needs a JIT atlas built at game start.
- The shared computePlayerStatus is the replay variant, so the
  alliance / target / embargo / nukeTargetsMe status icons stay off
  for the local player's perspective. Needs a live-aware variant.
2026-05-16 19:03:38 -07:00
evanpelle 2fec1e994e retire DynamicUILayer, restore warship UX on WebGL
DynamicUILayer was a canvas2D mix of: bonus-event gold/troops popups
(already duplicated by WebGL BonusPopupPass), nuke/transport telegraph
indicators (duplicated by WebGL passes), and a warship move-indicator
chevron drawn via MoveIndicatorUI. Delete the layer outright along
with its three orphan UI helpers (MoveIndicatorUI, NavalTarget,
NukeTelegraph).

That deletion uncovered a pre-existing bug from the "migrate away from
canvas" commit: warship select/move no longer worked. The deleted
UnitLayer had owned the click flow that emits MoveWarshipIntentEvent.
Re-add the flow inside UILayer (which already tracks selected /
multi-selected warships for its selection box): MouseUpEvent →
move-multi → move-single → select-nearest, plus shift+drag box
complete and select-all hotkey.

Wire MoveWarshipIntentEvent → view.showMoveIndicator(tx, ty, ownerID)
in mountWebGLDebugRenderer so the WebGL MoveIndicatorPass draws the
converging-chevron animation at the move target, colored by the
warship's owner. mountWebGLDebugRenderer now takes gameView + eventBus
to resolve the owner and subscribe.
2026-05-16 18:51:34 -07:00
evanpelle 8955be7667 fix: store embargoes as smallID numbers (drop string[] wart)
PlayerState.embargoes was string[] of stringified smallIDs — the
renderer parsed each entry with parseInt() to use as an array index.
Flagged in the integration handoff as something that should be number[].

Switch to number[] end-to-end: renderer type, relation-matrix derive
(no parseInt), PlayerView.setEmbargoSmallIDs / hasEmbargoAgainst
(numeric Array.includes, no String() temporaries), and GameView's
embargo translation pass. Also updates the PlayerView test that pinned
the old format.
2026-05-16 17:45:29 -07:00