VariableVince f304141338 Fix/refactor/optim(StructureIconsLayer): restore structure icons after context loss, use WebGL/WebGPU/Canvas, and some improvements (#3654)
## Description:

StructureIconsLayer and StructureDrawingUtils fixes and improvements.
Most notably have it restore structure icons after webGL context loss.

Inspired by @Skigim's
https://github.com/openfrontio/OpenFrontIO/pull/3339,
https://github.com/openfrontio/OpenFrontIO/pull/3480. Fixes his
https://github.com/openfrontio/OpenFrontIO/issues/3207, contains only
those fixes from the Issue that are actually valid and needed fixes, on
top of his earlier merged PR.

### CONTAINS (partly written by AI, excuse the exaggerated language)

**1.**
* ** AutoDetectRenderer: ** now, if Hardware Acceleration is unavailable
or disabled, Structure Icons will be displayed using Canvas renderer.
Otherwise it will use either WebGL or WebGPU, depeding on which is
available. PixiJS currently prefers WebGL but it will switch this to
WebGPU at one point. We can also force it to WebGPU as explained in the
comment.
* ** Canvas: ** on Canvas, what doesn't work is gracefully skipped. The
non-working parts will be fixed, see this issue in their repo, but until
then it will work fine for us anyway:
https://github.com/pixijs/pixijs/issues/11981
* **WebGPU Context Loss:** PixiJS doesn't restore this context itself.
Instead we do it by calling setupRenderer again on device loss.
* **WebGL Context Loss:** To know when we need to restore the layer,
don't use native event (`webglcontextrestored`) but use PixiJS's
internal hook (`this.renderer.runners.contextChange`). This prevents our
cache-clearing commands from interrupting Pixi while it's still busy
rebuilding its internal GL State Machine buffer. With links severed, we
need to clear and rebuild all icons to restore them.
* **WebGL Context existance Check (`this.renderer.context?.isLost`):**
This prevents a crash in PixiJS. Fixes black map background and all
graphics frozen, which has been reported a few times. Issue created in
their repo: https://github.com/pixijs/pixijs/issues/12032.
* **Redraw:** for Canvas context restore or on Alt-R, a call from
GameRenderer now actually restores icons. Also called for WebGPU device
loss and after contextChange WebGL restoration. Checks for WebGL
context.isLost so a calls from Alt-R etc won't meddle while GL context
is lost.
* **Orphaned Object Leaks:** In PixiJS v8, `Container.destroy()` does
*not* recursively destroy its children. This PR explicitly adds
`.destroy({ children: true })` inside icon deletion states. This stops
thousands of `PIXI.Sprite` and `PIXI.BitmapText` child nodes from
leaking and choking Pixi when it attempts a WebGL restore.
* **Texture Lifecycle:** Invalidate caching logic in `SpriteFactory` now
correctly executes `.destroy(true)` on `PIXI.Texture` objects.
Previously, they were only deleted from the textureCache Map, retaining
a phantom grip on GPU memory buffers.
* **Don't remove PIXI.Texture.EMPTY from textureCache: `createTexture()`
in `SpriteFactory` stores `PIXI.Texture.EMPTY` (a singleton) in
`textureCache` when a structure type has no known shape. When not
preventing removal of the EMPTY texture, `clearCache()` would call
`texture.destroy(true)` on PixiJS's shared global empty texture,
breaking all sprites in the renderer that fall back to it.

**2. Small Memory/Perf Optimizations**
* **The Shared 2D Canvas Optimization:** To prevent allocating endless
tiny `<canvas>` elements every time a structure color is loaded,
`SpriteFactory` now utilizes a cleanly shared `colorCanvas` singleton.
To keep this safe from hardware acceleration crashes (where the 2D
context dies alongside WebGL), it accurately nullifies itself in
`clearCache()` and lazily instantiates on the next call
(`getImageColored()`).
* **Bypassing Inefficient Textures Cache:** Now passing the `skipCache:
true` argument implicitly to dynamic UI elements via
`PIXI.Texture.from(structureCanvas, true)`.
* **Zero-Allocation Filters (No more GC Stutters):** `renderGhost()`
previously spawned numerous `new OutlineFilter(...)` WebGL shaders when
hovering over invalid tiles, compounding to many leaked Shader Programs.
We hoisted these filters to static class properties initialized once,
and went a step further: hoisted the wrapping Array structures too
(`filterRedArray`, `filterGreenArray`). This eliminates many pointless
micro-allocations and GC sweeps entirely.

**BEFORE, for webGL:**
https://youtu.be/durJxNFNePs

**AFTER, for WebGL:**
https://youtu.be/VnYEFMx4gfM

**AFTER, for Canvas:**
https://youtu.be/zT720oKxcaI

**AFTER, for WebGPU:**
https://youtu.be/J09Wee2qTs8

The performance optimizations weren't well measurable in my tests but
there's no downgrade at least. WebGPU should bee better than WebGL when
we would force it but again, currently PixiJS prefers WebGL hardcoded so
only if we disallow WebGL will it use WebGPU if it is available,
otherwise fallback gracefully to Canvas still.

Canvas skips parts gracefully, as long as the non-breaking issue exists
in PixiJS (as explained above):
<img width="952" height="705" alt="AFTER Canvas in Firefox skips
non-supported gracefully"
src="https://github.com/user-attachments/assets/17e8d8ab-05dc-47cb-ab11-f0f4d015a42a"
/>

## 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: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-29 20:40:02 -06:00
2025-06-22 08:14:08 -07:00
2025-05-15 23:09:39 -04:00
2026-04-29 12:49:19 -06:00
2025-03-06 15:50:29 -08:00
2025-05-15 23:09:39 -04:00
2026-03-23 13:59:34 -07:00
2026-04-29 12:49:19 -06:00
2026-01-21 10:00:55 -08:00
2026-01-08 13:34:18 -08:00
2026-04-29 13:52:36 -06:00
2026-04-01 20:03:39 -07:00
2026-04-29 17:17:33 -06:00

OpenFrontIO Logo

OpenFront.io is an online real-time strategy game focused on territorial control and alliance building. Players compete to expand their territory, build structures, and form strategic alliances in various maps based on real-world geography.

This is a fork/rewrite of WarFront.io. Credit to https://github.com/WarFrontIO.

CI Crowdin CLA assistant License: AGPL v3 Assets: CC BY-SA 4.0

License

OpenFront source code is licensed under the GNU Affero General Public License v3.0

Current copyright notices appear in:

  • Footer: "© OpenFront and Contributors"
  • Loading screen: "© OpenFront and Contributors"

Modified versions must preserve these notices in reasonably visible locations.

See the LICENSE for complete requirements.

For asset licensing, see LICENSE-ASSETS.
For license history, see LICENSING.md.

🌟 Features

  • Real-time Strategy Gameplay: Expand your territory and engage in strategic battles
  • Alliance System: Form alliances with other players for mutual defense
  • Multiple Maps: Play across various geographical regions including Europe, Asia, Africa, and more
  • Resource Management: Balance your expansion with defensive capabilities
  • Cross-platform: Play in any modern web browser

📋 Prerequisites

  • npm (v10.9.2 or higher)
  • A modern web browser (Chrome, Firefox, Edge, etc.)

🚀 Installation

  1. Clone the repository

    git clone https://github.com/openfrontio/OpenFrontIO.git
    cd OpenFrontIO
    
  2. Install dependencies

    npm run inst
    

    Do NOT use npm install nor npm i but instead use our npm run inst. It runs the safer npm ci --ignore-scripts to install dependencies exactly according to the versions in package-lock.json and doesn't run scripts. This can prevent being hit by a supply chain attack.

🎮 Running the Game

Development Mode

Run both the client and server in development mode with live reloading:

npm run dev

This will:

  • Start the webpack dev server for the client
  • Launch the game server with development settings
  • Open the game in your default browser (to disable this behavior, set SKIP_BROWSER_OPEN=true in your environment)

Client Only

To run just the client with hot reloading:

npm run start:client

Server Only

To run just the server with development settings:

npm run start:server-dev

Connecting to staging or production backends

Sometimes it's useful to connect to production servers when replaying a game, testing user profiles, purchases, or login flow.

To replay a production game, make sure you're on the same commit that the game you want to replay was executed on, you can find the gitCommit value via https://api.openfront.io/game/[gameId]. Unfinished games cannot be replayed on localhost.

To connect to staging api servers:

npm run dev:staging

To connect to production api servers:

npm run dev:prod

🛠️ Development Tools

  • Format code:

    npm run format
    
  • Lint code:

    npm run lint
    
  • Lint and fix code:

    npm run lint:fix
    
  • Testing

    npm test
    

🏗️ Project Structure

  • /src/client - Frontend game client
  • /src/core - Shared game logic
  • /src/server - Backend game server
  • /resources - Static assets (images, maps, etc.)

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Request to join the development Discord.
  2. Fork the repository
  3. Create your feature branch (git checkout -b amazing-feature)
  4. Commit your changes (git commit -m 'Add some amazing feature')
  5. Push to the branch (git push origin amazing-feature)
  6. Open a Pull Request

🌐 Translation

Translators are welcome! Please feel free to help translate into your language. How to help?

  1. Join the translation Discord
  2. Go to the project's Crowdin translation page: https://crowdin.com/project/openfront-mls
  3. Login if you already have an account / Sign up if you don't have one
  4. Join the project
  5. Select the language you want to translate in. If your language isn't on the list, click the "Request New Language" button and enter the language you want added there.
  6. Translate the strings

Feel free to ask questions in the translation Discord server!

Project Governance

  • The project maintainer (evan) has final authority on all code changes and design decisions
  • All pull requests require maintainer approval before merging
  • The maintainer reserves the right to reject contributions that don't align with the project's vision or quality standards

Contribution Path for New Contributors

To ensure code quality and project stability, we use a progressive contribution system:

  1. New Contributors: Limited to UI improvements and small bug fixes only

    • This helps you become familiar with the codebase
    • UI changes are easier to review and less likely to break core functionality
    • Small, focused PRs have a higher chance of being accepted
  2. Established Contributors: After several successful PRs and demonstrating understanding of the codebase, you may work on more complex features

  3. Core Contributors: Only those with extensive experience with the project may modify critical game systems

How to Contribute Successfully

  1. Before Starting Work:

    • Open an issue describing what you want to contribute
    • Wait for maintainer feedback before investing significant time
    • Small improvements can proceed directly to PR stage
  2. Code Quality Requirements:

    • All code must be well-commented and follow existing style patterns
    • New features should not break existing functionality
    • Code should be thoroughly tested before submission
    • All code changes in src/core MUST be tested.
  3. Pull Request Process:

    • Keep PRs focused on a single feature or bug fix
    • Include screenshots for UI changes
    • Describe what testing you've performed
    • Be responsive to feedback and requested changes
  4. Testing Requirements:

    • Verify your changes work as expected
    • Test on multiple systems/browsers if applicable
    • Document your testing process in the PR

Communication

  • Be respectful and constructive in all project interactions
  • Questions are welcome, but please search existing issues first
  • For major changes, discuss in an issue before starting work

Final Notes

Remember that maintaining this project requires significant effort. The maintainer appreciates your contributions but must prioritize long-term project health and stability. Not all contributions will be accepted, and that's okay.

Thank you for helping make OpenFront better!

S
Description
Languages
TypeScript 91.3%
GLSL 2.6%
JavaScript 1.9%
HTML 1.7%
CSS 1%
Other 1.5%