Commit Graph

133 Commits

Author SHA1 Message Date
MushroomLamp d76691372c Feature/reuse private lobby (#4536)
**Add approved & assigned issue number here:**

Resolves #4476 

## Description:

Lets a private-lobby host reuse the same group for **back-to-back games
without
re-sharing the invite link**.

**Flow:** in a private game, the host clicks a **"New lobby"** button in
the
top-right bar (next to pause). The game's **server** creates a fresh
private
lobby (same creator, default settings) and broadcasts its id to everyone
still
connected. Non-hosts get a one-click **"Join"** banner at the top of the
screen;
the host is taken straight back to the host view for the new lobby. The
chain
can repeat indefinitely.

### Key design decision: the server creates and broadcasts the successor
lobby

The successor lobby is minted by the **finished game's server**, not the
host's
browser. The old game's server is the only thing still connected to
every player, so it has to be what announces the new
lobby; because it also *creates* that lobby, the id everyone is
redirected to is
**authoritative**. A real lobby the server just made for the
authenticated
creator, not an id a client handed it to trust and fan out. The request
is
**creator-only** and **idempotent** per game, and every successor is
wired the
same way, so the group can keep playing game after game.

### How it works

1. Host clicks "New lobby" (host + private only) → confirm dialog → the
client
   sends a `create_next_lobby` message.
2. The game server verifies the sender is the lobby creator, mints a
successor
private lobby on the same worker, stores it (idempotent), and broadcasts
a
   `new_lobby` message with the new id to all connected clients.
3. Each client reacts: the host is navigated to the new lobby's host
view
(`/…/game/<id>?host`); everyone else sees a dismissible "Host started a
new
   lobby — Join" banner that navigates to the join URL in one click.

Two new Zod wire messages in `src/core/Schemas.ts` (`create_next_lobby`,
`new_lobby`) carry the request and the broadcast.

### Screenshots

<table>
  <tr>
    <td width="50%" align="center" valign="top">
<img width="220" alt="In-game New lobby button"
src="https://github.com/user-attachments/assets/9a4d4425-a7f6-4b3a-9c4f-9205300b1e5b"
/><br />
<sub><b>1.</b> In-game <b>New lobby</b> button (top-right, next to
pause) — shown only to the host of a private lobby</sub>
    </td>
    <td width="50%" align="center" valign="top">
<img width="320" alt="Confirmation dialog"
src="https://github.com/user-attachments/assets/fa023f5a-58e8-439b-8899-5150235a1e8c"
/><br />
<sub><b>2.</b> Confirmation so a stray click doesn't pull everyone into
a new lobby</sub>
    </td>
  </tr>
  <tr>
    <td colspan="2" align="center">
<img width="100%" alt="Join banner for non-hosts"
src="https://github.com/user-attachments/assets/7e5ef490-aa7e-4449-add9-e857fe273bde"
/><br />
<sub><b>3.</b> Everyone else gets a one-click <b>Join</b> banner at the
top of the screen</sub>
    </td>
  </tr>
  <tr>
    <td colspan="2" align="center">
<img width="330" alt="Host view for the new lobby"
src="https://github.com/user-attachments/assets/9920b070-4ed3-41f8-9345-78778b4648a7"
/><br />
<sub><b>4.</b> The host lands back in the host view for the brand-new
lobby</sub>
    </td>
  </tr>
</table>


### Design Decisions

**A. The server creates & broadcasts the successor, not the client.**
The finished game's server mints the successor and broadcasts its id.
Why this
and not "host's browser calls `POST /api/create_game`, then asks the
server to
relay the id"?
- The broadcast id is **authoritative/verified**: it's a real lobby the
server
just created for the **authenticated** lobby creator (creator identity
comes
from the JWT the game already holds), not an arbitrary id a client hands
the
  server to fan out to everyone.
- The **old game server is the only thing still connected to all the
players**,
so it must be the one to broadcast. Having it also create the lobby
keeps it
to one authoritative round-trip instead of "client creates, then client
asks
  server to trust an id it didn't make."
- The server can **authorise** (only the creator) and stay
**idempotent**.

**B. The successor starts with default settings (not a copy of the old
game).**
A deliberate scope choice. The host lands in the normal host view and
reconfigures. Copying the exact config would mean reverse-mapping every
`GameConfig` field back into the host-modal controls, which I thought
would be out of scope for this PR. Same **creator**
is preserved; same **settings** intentionally is not.

**C. It's a brand-new lobby, not the same game resurrected.**
This directly follows @evanpelle's guidance on the issue: *"A 'lobby' is
really
just a game that hasn't started yet. So making a persistent lobby isn't
really
possible. I think instead having a simple way to transfer players to a
new lobby
is probably the way to go."* A `GameServer` runs exactly one game
(start → end → archive), so rather than reworking that lifecycle to
resurrect the
old game, the server spins up a fresh successor lobby and transfers the
group
into it, which is also why the feature is framed as "reuse the group,"
not
"reuse the game object."

**D. The host returns via a `?host` URL flag + full reload ("attach
mode").**
Navigating to a normal join URL (`/game/<id>`) always lands you in the
**join**
view, which has no Start button. So the host can't just use the join
URL. The
`?host` flag routes the creator to the **host view** instead
(`Main.handleUrl` → `HostLobbyModal` in "attach" mode, which binds to
the
existing lobby id and skips creating a new one). A full reload is used
because
it cleanly tears down the finished game and mirrors the existing
win-screen
"Requeue" button's `window.location.href` pattern.

**E. Each successor can spawn its own successor (recursive factory).**
The first version only chained **one** generation. A spawned lobby had
no
factory of its own, so the *second* "New lobby" click did nothing
(button just
greyed out). Fixed by `wireSuccessorLobby` (a small dependency-injected
helper)
that wires every successor the same way. This is the whole point of the
issue
("back-to-back games"), so it has its own regression test.

**F. Private-only.**
The successor factory is installed **only** on the private `POST
/api/create_game` path in `Worker.ts`. Public games (scheduled by the
master)
and singleplayer never get a factory, so `handleCreateNextLobby` is a
no-op for
them. The client button is also gated on `isLobbyCreator &&
isPrivateLobby`.

**G. In-game button + confirm; the win-modal button was removed.**
An earlier version put the "New lobby" button on the win screen. I moved
it to
the in-game bar so the host can reuse the lobby **at any time** (without
dying
or waiting for the game to end), and added a **confirm** (matching the
adjacent
Exit button) so a stray click next to pause/exit doesn't yank everyone
into a
new lobby. The win-screen button became redundant and was removed.


### Testing

- **Unit tests:** wire-message schema round-trips
(`tests/NewLobbyMessages.test.ts`); the server handler — authorisation,
  broadcast, idempotency — against a real `GameServer`
(`tests/server/CreateNextLobby.test.ts`); and successor **chaining**
across
  multiple generations (`tests/server/SuccessorLobby.test.ts`).
- **Full suite:** `npm test` passes — **1782 tests across 154 files**.
- **Manual:** created a private lobby with multiple clients and played
consecutive games via the button; verified non-hosts get the Join
banner, the
host lands back in the host view, and the chain works for 3+ games in a
row.

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

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

MushroomLamp

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 15:51:49 -07:00
Evan d3ad1f51bd fix(crazygames): guest username on logout, hide fullscreen, in-game pop-ups (#4538)
Addresses four requests from the CrazyGames platform team.
Supersedes #4520 (closed when its branch was renamed to `crazygames`).

## 1. Username reverts to guest on CrazyGames logout
When a player logged into their CrazyGames account and then logged out,
the username stayed the CrazyGames name. It now reverts to a fresh guest
(`AnonXXX`) name. A `crazyGamesLoggedIn` flag guards this so it only
fires on a real login→logout transition (not on the initial "not logged
in" state, which would otherwise wipe a stored name).

## 2. Fullscreen button hidden on CrazyGames
CrazyGames provides its own fullscreen control in the game frame, so our
in-game fullscreen button is now hidden when running on CrazyGames (and
unchanged everywhere else).

## 3. Native browser prompts → in-game pop-ups
The `showInGameConfirm()` / `showInGameAlert()` helpers (promise-based,
callable from non-Lit contexts like WebSocket/popstate handlers) drive
the existing shared `<confirm-dialog>` component, so styling stays
consistent with the rest of the app. Every native `confirm()`/`alert()`
shown **during gameplay** now uses it:
- Exit-game confirmation (right sidebar + browser-back path)
- Kick-player confirmation
- Host-left notice (dispatches leave-lobby only after dismiss,
preserving the old blocking UX)
- Connection-refused notice (also removes a stale `// TODO: make this a
modal`)

## 4. `gameplayStop` when Settings menu / pop-up is open
- The pop-up helpers report `gameplayStop()` while shown and
`gameplayStart()` on dismiss.
- Settings + Graphics Settings modals now report `gameplayStop` whenever
the menu is open — previously it only fired when the modal *also* paused
the sim (singleplayer / lobby-creator), so regular multiplayer players
never triggered it. Also fixes graphics-settings so gameplay resumes
correctly on close for those players.

## Scope
Per request, this covers **in-game dialogs only**. The main-menu native
alerts (store/checkout, account, subscriptions, friends, login) were
intentionally left as-is.

## Testing
- `tsc --noEmit`, `eslint`, and `prettier --check` all pass.
- Verified the confirm and alert pop-ups render correctly in the running
app (shared `<confirm-dialog>`, danger/warning variants), resolve their
promises, and tear down their portal.
- CrazyGames-iframe-only behaviors (username revert, `gameplayStop`,
fullscreen hiding) are verified by code inspection — they require
running inside the actual CrazyGames frame.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 12:02:37 -07:00
Evan 181368f962 Add live game stats endpoint to the admin bot API (#4399)
## Summary

The game simulation runs **client-side**, so the server can't directly
see what's happening in a running game. This adds a way for the admin
bot to observe a live game: clients report a live stats snapshot every
~10s, the server reaches consensus on it (reusing the winner's vote
mechanism), and a new admin-bot endpoint serves it.

## How it works

1. **`LiveStatsController`** (client) emits a snapshot every **100
turns** (~10s at 100ms/turn) — only deterministic sim values, with
players sorted by clientID, so in-sync clients produce an identical
payload.
2. The snapshot is sent as a new **`live_stats`** wire message wrapping
a `LiveStats` object (`turn` + per-human-player
`tilesOwned`/`troops`/`gold`/`isAlive`/`team`).
3. **`GameServer.handleLiveStats`** tallies a per-turn **IP-weighted
majority vote** — the same consensus the winner uses — and keeps the
latest agreed snapshot.
4. **`GET /api/adminbot/game/:id/stats`** returns it, enriched with
usernames the server already holds. `liveStats` is `null` until the
first consensus.

The winner's vote tally was extracted into a small reusable
**`VoteRound`** (`src/server/VoteTally.ts`) and is now used for both
winner and live-stats consensus.

Names are deliberately **excluded** from the voted payload (they vary
per client under name anonymization, which would break exact-match
consensus); the server joins `clientID → username` instead.

## Changes

- `src/server/VoteTally.ts` *(new)* — reusable IP-weighted `VoteRound`
- `src/core/Schemas.ts` — `PlayerLiveStatsSchema`, `LiveStatsSchema`,
`ClientSendLiveStatsSchema` + unions
- `src/client/controllers/LiveStatsController.ts` *(new)* — per-100-turn
snapshot reporter
- `src/client/Transport.ts` — `SendLiveStatsEvent` + sender
- `src/client/hud/GameRenderer.ts` — register the controller
- `src/server/GameServer.ts` — refactor winner onto `VoteRound`; add
live-stats consensus + `liveStats()` accessor
- `src/server/AdminBotRoutes.ts` — `GET …/stats` endpoint

## Testing

- **Unit:** `tests/server/VoteTally.test.ts` (majority/dedup/ties),
`tests/server/LiveStats.test.ts` (consensus, disagreement, per-client
dedup, stale-turn rejection, turn advance, out-of-sync exclusion, +
endpoint 200/404/400). Full suite green (`npm test`), typecheck + lint
clean.
- **Manual e2e** against the dev server: created an admin-bot game,
joined it in a browser, force-started via `toggle_game_start_timer`, and
confirmed `GET …/stats` returned the consensus snapshot with username
enrichment and an advancing `turn`. Also verified wrong-worker → 400 and
missing-key → 401.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:21:52 -07:00
Zixer1 8ce5f3439c feat: kick_player can target a publicId (admin bot) (#4403)
## Description:

Add an optional `targetPublicId` to KickPlayerIntent; the server
resolves it against the connected clients to the live clientID, then
kicks as before. Existing clientID targeting (lobby / in-game kick) is
unchanged. That way you can kick player with both the clientID and
playerID

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

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

zixer._
2026-06-24 14:34:17 -07:00
Evan aa4b490e68 Simplify WebGL renderer integration: remove dead extension code, untangle GameView naming (#4240)
## Summary

The WebGL renderer was adapted from an external extension and carried a
lot of machinery this integration never uses (replay playback, its own
input/event system, a GL radial menu). This PR is two mechanical cleanup
passes with **no behavior change**: delete the dead code, then untangle
the `GameView` naming collision.

**78 files, +142 / −2,197.**

### Pass 1 — remove dead extension baggage

- **Replay/copy mode**: `FrameData.tileMode` was hard-coded `"live"`;
the copy branches in `frame/Upload.ts`, `UploadOptions` (never passed),
`applyFullFrame`/`applyFullTiles`/`applyDelta` on the facade and
`GPURenderer`, `HeatManager.resetForSeek`, and the seek-upload methods
on `TerritoryPass`/`TrailPass` were all unreachable. Also deletes
`types/Replay.ts`, `types/FrameSource.ts`, `types/GameUpdates.ts`,
`types/Game.ts` (imported only by the types barrel).
- **FrameEvents**: trimmed from 14 fields to the 3 actually populated
and read (`deadUnits`, `conquestEvents`, `bonusEvents`). The other 11
fed the extension's stats system and were never written or read here.
- **GL radial menu**: `RadialMenuPass`, its 4 shaders, and ~10 API
methods on facade + renderer had zero callers — the game uses the DOM/d3
radial menu in `hud/layers/RadialMenu.ts`. The pass was constructed and
drawn every frame for nothing.
- **Facade event system**: `GameViewEventMap` defined 10 event types
(`click`, `hover`, `scroll`, …) but only `contextrestored` was ever
emitted — input actually flows through `InputHandler` → EventBus →
controllers. Replaced the listener map with a single `onContextRestored`
callback and deleted `Events.ts`. Also fixed the stale header comment
claiming the facade handles user interaction.
- **Unused API surface**: removed ~20 facade/renderer methods with zero
callers (camera passthroughs like
`panTo`/`zoomTo`/`fitMap`/`screenToWorld`, hit-testing queries, SAM
replay setters, `setSelectedUnit`, `clearFx`/`setFxTimeFn`,
`onFrame`/`afterRender`/fps tracking).

Deliberately left alone: `Camera`'s pan/zoom primitives (building blocks
for a possible future camera unification) and the `timeFn` plumbing
inside the FX passes (deeply embedded as defaults; only the dead
renderer-level wrappers were removed).

### Pass 2 — untangle the three GameViews

- `render/gl/GameView.ts` → **`MapRenderer.ts`** (class `MapRenderer`).
Every importer was already aliasing it as `WebGLGameView` to dodge the
collision with the simulation-mirror `GameView` in `client/view/`, so
this removes aliasing rather than adding churn. `render/CLAUDE.md`
updated.
- Deleted the `src/core/game/GameView.ts` back-compat shim (its own TODO
asked for this). All 51 importers now import from `src/client/view/`
directly via a new 3-line barrel `view/index.ts`.

## Test plan

- `tsc --noEmit` clean, `eslint` clean
- Full test suite passes (1,385 + 65 server tests)
- Manual verification via headless Chromium: started a singleplayer game
and confirmed the renderer works end-to-end — terrain draws, spawn-phase
overlay shows, territories fill with borders after spawning, player
names/flags render, no renderer console errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 14:21:24 -07:00
FrederikJA 19db66f424 Delayed lobby start (#4184)
Resolves #4169

## Description:

Adds a delayed lobby start option.
Utilizes the same system as for public lobbies.
The default for the option is for lobbies to take 3 seconds to start,
however this can easily be changed.

The current setting is controlled through an enable-disable slider,
however there are multiple other options for how to control this.
For example we could do a slider, an input field, a dropdown etc. And i
dont necessarily know if the currently implemented option is the best.

Furhtermore im not sure if i have used the language file completely
correctly. There is now a duplicate field for both private and public
lobby. However there is not category shared between the two. So i
decided to reuse the field from public for private games, as this
simplified the code a bit.

**Host video**

https://github.com/user-attachments/assets/6f3db6e4-7323-4fad-8544-efb8cef4d969

**Non-host video**

https://github.com/user-attachments/assets/ee02a072-1f42-4dde-a5d9-120fda862eb7

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


## Please put your Discord username so you can be contacted if a bug or
regression is found:
FrederikJA
2026-06-12 12:22:03 -07:00
Evan 275fd0dccc refactor: collapse per-env Configs into ClientEnv + ServerEnv (#3906)
## Description:

This is a refactor to simplify config handling.

Replaces the per-environment DevConfig/PreprodConfig/ProdConfig class
hierarchy with two static classes: ClientEnv (browser main thread, reads
from window.BOOTSTRAP_CONFIG) and ServerEnv (Node server, reads from
process.env). The four config classes are deleted, the abstract
DefaultServerConfig is gone, and DefaultConfig is renamed to Config.

The values that flow server → client (gameEnv, numWorkers,
turnstileSiteKey, jwtAudience, instanceId) used to be baked into the
hardcoded per-env classes. They're now real env vars on the server,
embedded into a single window.BOOTSTRAP_CONFIG object in index.html at
request time (alongside the existing gitCommit/assetManifest/cdnBase
globals, which moved into the same object), and read back by ClientEnv
on the client. The dev defaults previously hidden inside DevServerConfig
are now explicit in start:server-dev (NUM_WORKERS=2,
TURNSTILE_SITE_KEY=1x..., JWT_AUDIENCE=localhost, etc.) and in
vite.config.ts's html plugin inject.data. Production deploys plumb
NUM_WORKERS and TURNSTILE_SITE_KEY through deploy.yml (GitHub vars) into
the remote env file; JWT_AUDIENCE is derived from DOMAIN in deploy.sh.
The dynamic /api/instance endpoint is gone — INSTANCE_ID rides along in
BOOTSTRAP_CONFIG now.

ServerEnv is the only thing server code touches; ClientEnv is
browser-only. The two classes have intentional overlap (env, numWorkers,
jwtIssuer, gameCreationRate, workerIndex, etc.) since they derive
identical logic from different sources — there's a TODO in each to
consolidate via a shared helper later. The game-logic Config no longer
stores a ServerConfig/ClientEnv reference and its serverConfig() getter
is gone; the one caller (MultiTabModal) now reads ClientEnv.env()
directly. Worker init no longer carries server-config values since
nothing in the worker actually reads them.

## 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-11 19:24:01 -07:00
Evan 2994a5f848 Start game via WebSocket intent (#3794)
## Description:

Replaces the HTTP POST /api/start_game/:id endpoint with a WebSocket
intent, making private game start consistent with how kick_player and
update_game_config already work. Also verifies that only the lobby
creator can start a game.

## 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-04-29 13:26:14 -06:00
Ivan Batsulin 29a1e8dfda feat: multi-warship selection with Shift+drag box (#3677)
Resolves #3666

## Description:

Adds RTS-style box selection for warships. Hold Shift and drag (desktop)
or long-press and drag (touch/mobile) to draw a selection rectangle —
all player-owned warships inside get selected at once. A subsequent
click/tap on water sends them all to that location.

- `SelectionBoxLayer` — pixel-dashed rectangle in world-space, player
territory color; shared between desktop and touch
- `UILayer` — same pulsing selection outline on each box-selected
warship; clears correctly when switching between single/multi selection
- `UnitLayer` — finds warships in screen rect, filters inactive ships
before sending; touch support included
- `InputHandler` — Shift+drag and touch long-press+drag both emit
selection box events; cursor becomes crosshair on Shift; discards active
ghost structure on Shift press; configurable via `shiftKey` keybind
- `Transport` — single atomic `move_multiple_warships` intent (no split
on socket drop)
- `Schemas` + `ExecutionManager` + `MoveMultipleWarshipsExecution` —
server fans out atomic intent into individual `MoveWarshipExecution` per
ship
- `DynamicUILayer` — `MoveIndicatorUI` chevron animation on target tile
for both single and multi move
- `UnitDisplay` — warship tooltip Shift hint via `translateText`
- `HelpModal` — new hotkey row: Shift + drag → select multiple warships

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

## UI update
### Mouse + Keyboard


https://github.com/user-attachments/assets/3f35ab5e-1f3c-4c5d-bc4f-aabccf64dc60

### Touch


https://github.com/user-attachments/assets/0d6aec3f-44fa-4fee-b5c6-b267b9b14d79

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

fghjk_60845
2026-04-21 14:06:07 -07:00
Ryan 1049b7e7dc Clan System Part 1 (#3276)
## Description:

Properly split out clantags and usernames, a clantag should not be part
of a username.

<img width="285" height="286" alt="image"
src="https://github.com/user-attachments/assets/8ac56e82-b12c-4fc0-9774-e445252a6e61"
/>

https://api.openfront.dev/game/ojkqZFb2


<img width="296" height="596" alt="image"
src="https://github.com/user-attachments/assets/85152f80-c111-4f87-b85b-8516c9c6137b"
/>


https://api.openfront.dev/game/MF32BkVc


requires;
https://github.com/openfrontio/infra/pull/264

## 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-17 15:55:47 -07:00
Mattia Migliorini f362e47413 Cancel nukes when accepting alliance via radial menu (#3155)
Resolves #3154

## Description:

#2716 introduced nuke cancellation logic on alliance acceptance via
`AllianceRequestReplyExecution`. The radial menu action, though, calls
`AllianceRequestExecution` instead, which accepts the alliance if a
request has already been made by the other player.

This PR moves the nuke cancellation logic to `GameImpl`, hooking into
the `acceptAllianceRequest` method, therefore accounting for every
alliance acceptance, regardless of the specific action that brought to
that.

## Please complete the following:

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

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

deshack_82603
2026-02-16 11:10:26 -08:00
Ryan 8dcc7cfb9a Fix client reconnection after page refresh (#3117)
## Description:

- Removed all code related to generating a client ID on the client. The
server now assigns the client ID and sends it to the client in lobby
messages.

## Please complete the following:

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

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

w.o.n
2026-02-09 01:10:11 +00:00
Arkadiusz Sygulski 18fb513326 Pathfinding refinements (#2959)
## Description:

### Short path for multi-source HPA*

Math was not mathing, increased the bounds to 260x260, it is a bit
slower but should work better. The short path was breaking when player
owned a lot of shores. This is because the bounding box of tiles with
less than 120 distance + 10 padding could be as big as 260x260 and the
optimized array was set to 140x140. I made mistake of calculating it as
`2 * (60 + 10)` instead of `2 * (120 + 10)`.

### LoS path refinement

Previously, we ran 2 passes of LoS smoothing on the path. However, since
we are effectively tracing the same path, the line of sight is
essentially the same. This PR makes second line of sight stop on water
tiles with magnitude `n + 1` compared to first path. Practically, this
means it'll attempt LoS exactly 1 tile after previous corner. See
screenshot.

<img width="1299" height="1151" alt="image"
src="https://github.com/user-attachments/assets/726be236-1ff8-406c-896a-02902a762ab0"
/>

### SendBoatAttackIntentEvent

The flow of sending transport ships is currently strange. This PR makes
the flow more sane.

**Old flow**
```
- Player clicks TARGET tile, it can be deep inland
- Client asks Worker for the best START tile to TARGET tile
- Worker answers `false`, since the tile is inland
- Client sends BoatAttackIntent with START=false and TARGET tiles set
- Worker accepts BoatAttackIntent, computes DESTINATION as closest shore to TARGET
- Worker re-computes best START to DESTINATION
- Worker sends boat from START to DESTINATION
```

**New flow**
```
- Player clicks TARGET tile, it can be deep inland
- Client sends BoatAttackIntent with TARGET
- Worker accepts BoatAttackIntent, computes DESTINATION as closest shore to TARGET
- Worker computes START as the best tile to DESTINATION
- Worker sends boat from START to DESTINATION
```

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

moleole
2026-01-19 19:28:28 -08:00
Ryan c179249cdd [BugFix] Join Lobby Bugfix (#2947)
## Description:

If a WebSocket was "connecting", but the user un-clicks the lobby in
that time, it doesn't remove them from the lobby, and they would still
be put into the game.

@evanpelle needed for v29 imo.

It also fixes an issue where it wouldn't update your URL back to the
home url when unclicking the lobby (websocket issue or not).

This is a hotfix to fix that.

## Please complete the following:

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

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

w.o.n
2026-01-18 11:07:52 +00:00
Vahant Sharma b990fe6ae5 Use WebSocket intent for lobby game configuration updates (#2761)
Fixes #2758

## Description:
This PR migrates lobby configuration updates from the HTTP PUT
`/game/:id` endpoint to a WebSocket-based intent flow.

The lobby creator is already authenticated via the game WebSocket, so
updating configuration through intents removes redundant authentication
and aligns with existing real-time lobby actions such as `kick_player`
and `toggle_pause`.

## Changes Made
- Added `update_game_config` WebSocket intent schema
- Wired client → transport → server intent handling
- Refactored `putGameConfig()` to emit WebSocket intent instead of HTTP
fetch
- Preserved all existing validation, partial-update semantics, and
client-side debouncing
- Left the REST endpoint untouched for backward compatibility

## Testing
- All existing automated tests pass
- Manual verification completed:
  - Lobby creator can update all lobby settings
  - Non-creators are rejected
  - Updates are rejected after game start
  - Bots slider debounce (300ms) remains intact
  - No `PUT /api/game/:id` requests are made from the lobby UI

## 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
2026-01-01 16:46:53 -08:00
Ryan f1561df470 Bomb Direction (#2435)
Resolves #2434 

## Description:

Allows bomb direction to be inverted by pressing a hotkey - currently
"U".

**Check the issue for screenshots / videos.**

## 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: Evan <evanpelle@gmail.com>
Co-authored-by: iamlewis <lewismmmm@gmail.com>
2025-12-29 09:03:46 -08:00
YoussfeCantCode 1c52d20e83 Added pause functionality for private multiplayer games (#2657)
If this PR fixes an issue, link it below. If not, delete these two
lines.
Resolves #2491

## Description:
Adds pause/unpause functionality for private multiplayer games. Only the
lobby creator can pause the game, and all players see a pause overlay
when the game is paused.

  **Key features:**
- Lobby creator sees pause/play button in control panel (alongside
existing singleplayer/replay controls)
  - Server validates that only lobby creator can toggle pause
  - All players see "Game paused by Lobby Creator" overlay when paused
  - Game state freezes (no turn execution) while paused
  - Unpause resumes normal gameplay

  **Implementation details:**
- Server-side pause state (`isPaused`) prevents turn execution during
pause
- Each client receives `isLobbyCreator` flag in `GameStartInfo` to
show/hide pause button
- Added `TogglePauseIntent` that broadcasts to all clients via
`NoOpExecution`
  - New `PauseOverlay` component (shows in single player also)

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

furo18

<img width="1459" height="861" alt="Screenshot 2025-12-20 at 15 16 33"
src="https://github.com/user-attachments/assets/f5a3222f-f54b-473c-b0f6-104ce4c1e7a8"
/>
2025-12-26 10:21:18 -08:00
Evan dab04eddb4 bugfix: user receives unauthorized on reconnection (#2601)
## Description:
When rejoining, the client did not refresh the play token, so the jwt
could be expired, causing the server to reject it.

Also improved the error log when token fails

## 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
2025-12-11 20:00:53 -08:00
Evan 335a371fdb bugfix: singleplayer not starting (#2589)
#075c232 introduced a bug that prevented singplayer games from starting.

The `updateCallback` was not set for localserver, and local server
didn't send a start message when client sent a "rejoin".

## 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
2025-12-08 16:42:29 -08:00
Evan 3314ca16ce Turnstile: require token before joining a multiplayer game (#2572)
When user tries to join either a public or private multiplayer game, the
turnstile callback is triggered, and the turnstile token is passed to
the server when joining a game.

## 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
2025-12-08 16:16:31 -08:00
Evan 075c232d8a improve game websockt (re)connection (#2584)
Previously, the connection and reconnection logic were identical in
Worker.ts, so clients would need to be re-authorized for cosmetics etc
even when reconnecting. Now, on reconnect, Worker.ts only does
authentication - verifying the jwt is valid.

This will allow clients to require a valid turnstile token when first
connecting, and not when reconnecting after a broken ws connection.

## 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
2025-12-08 14:07:07 -08:00
Vivacious Box 6ce1835d77 Fix socket log (#2369)
## Description:

Fix null socket crashing the log

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

Mr.Box
2025-11-02 23:10:50 +00:00
Abdallah Bahrawi 380eab5d12 Implement Stop/Start trading with all (#2278)
## Description:
fixes #2275 

Added global Start/Stop trading; use your **player panel** to trigger
it.

<img width="370" height="540" alt="Screenshot 2025-10-23 184447"
src="https://github.com/user-attachments/assets/c3b7967e-ffdd-4f37-ba67-b60a602278ce"
/>

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

abodcraft1
2025-10-25 20:59:40 +00:00
evanpelle 5540eff24c Merge branch 'v26' 2025-10-14 20:24:15 -07:00
evanpelle 9ab35a0436 bugfix: don't use bigint for zod schema as it causes json parsing issues 2025-10-14 19:05:53 -07:00
Hiphex fee2f822ed Fix spelling typos and improve code quality (#2186)
## Description:
  This PR fixes several bugs and improves code quality:

- Fix spelling typos: "recieved" → "received" in Transport.ts and
GameServer.ts
  - Fix comment typo: "isn'te" → "isn't" in TerrainLayer.ts
- Improve WebSocket cleanup in Transport.ts leaveGame() by replacing
empty onclose handler with proper
   killExistingSocket() call
- Add console.warn for image decode failures in StructureLayer.ts
instead of silent catch
  - Remove commented dead code in DevConfig.ts

  ## Testing
All 288 tests pass. Development build completes successfully. No
breaking changes.

  ## Files Changed
  - src/client/Transport.ts
  - src/server/GameServer.ts
  - src/client/graphics/layers/TerrainLayer.ts
  - src/client/graphics/layers/StructureLayer.ts
  - src/core/configuration/DevConfig.ts

## Checklist:
- [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()
and I've added it to the en.json file (N/A - no user-facing text)
- [x] I have added relevant tests to the test directory (All 288
existing tests pass - no new tests needed for typo fixes)
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

  Discord: hiphex_33496

Hiphex

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-14 22:07:49 +00:00
evanpelle 584fa9fb5d add support for custom colors (#2103)
## Description:

Added a colors tab in territory patterns modal so players can select
their color.

Refactored the PrivilegeChecker, removed custom flag checks since we no
longer support custom flags.

<img width="479" height="345" alt="Screenshot 2025-09-27 at 5 01 17 PM"
src="https://github.com/user-attachments/assets/ad96da65-f0eb-4731-a861-e6e5fcb4566a"
/>
## 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
2025-10-09 20:47:20 -07:00
evanpelle fdb05bf777 Merge branch 'v25' 2025-09-26 10:53:57 -07:00
evanpelle a26585a47b Add support for colored patterns (#2062)
## Description:

Add support for colored territory patterns/skins

* Refactored & updated territory pattern rendering to render colored
skins
* rename public from pattern to skin (keep pattern name internally, too
difficult to rename)
* Moved all territory color logic to PlayerView
* Updated WinModal to show colored skins
* Refactored decode logic into a separate function: decodePatternData
* Refactored/updated how cosmetics are sent to server. Players now send
a PlayerCosmeticRefsSchema in the ClientJoinMessage.
PlayerCosmeticRefsSchema just contains names of the cosmetics, and the
server replaces the names/references with actual cosmetic data
* Refactored PastelThemeDark: have it extend Pastel theme so duplicate
logic can be removed.
* 

## 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
2025-09-18 20:00:15 -07:00
evanpelle d2314941fe Merge branch 'v25' 2025-09-18 11:26:53 -07:00
evanpelle defb6bb1d4 Store full game for singleplayer, add more validation (#2031)
## Description:

onunload allows up to 64kb, but reducing the number of hash messages and
compressing using gzip, we can reduce the GameRecord size to stay under
64kb. I played a 10 minute game and the compressed GameRecord was only a
few kb.

Also verify the game is singleplayer and has only 1 player

## 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
2025-09-08 18:14:08 -07:00
Kipstz 4b129a2f7f Add button for remove building (#1609)
## Description:

Added a red delete button with trash can icon to the right-click radial
menu that allows players to voluntarily delete their own units.

## 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
- [x] I have read and accepted the CLA agreement (only required once).

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

Kipstz

<img width="286" height="209" alt="image"
src="https://github.com/user-attachments/assets/85142be3-2aa5-4c84-ab30-0c68289c8f85"
/>

---------

Co-authored-by: Drills Kibo <59177241+drillskibo@users.noreply.github.com>
2025-09-03 16:06:07 -07:00
evanpelle b57a409b8a store & reference pattern by name (#1766)
## Description:

Store pattern by name instead of value. The worker replaces the pattern
name with it's base64 when joining. This ensures the client & server are
never out of sync after patterns are updated.

* removed resizeObserver on the territory modal, it was causing some
race conditions, and the modal is not resizable so it's unnecessary.

* Moved PatternSchema to CosmeticSchema
## 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
- [x] I have read and accepted the CLA agreement (only required once).

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

evan
2025-08-16 18:08:16 -07:00
evanpelle 3b8a36166a Remove workers & troop ratio bar, only have troops (#1676)
## Description:

The troop/worker ratio bar is almost never changed. so remove it and the
entire concept of workers. Now there is just troops.

Now players get a consistent 1k/s gold.

## 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
- [x] I have read and accepted the CLA agreement (only required once).

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

evan
2025-08-01 16:06:59 -07:00
floriankilian bd59cd61cb Private Lobbies: Add kick player functionality (#1436)
## Description:
Added player management features so lobby hosts can kick players from
private games. This includes both UI changes and backend work.

### What's new:
- Hosts can now kick players from private lobbies with a simple button
- Added host badges and remove buttons to the UI
- Made sure only hosts can kick people, and hosts can't kick themselves

### How it works:
- When someone creates a private game, they automatically become the
host
- Kicking happens through WebSocket "kick-player" events
- Server checks that you're actually the host before letting you kick
anyone

<img width="1291" height="871" alt="Screenshot 2025-07-15 002114"
src="https://github.com/user-attachments/assets/ea575f83-a0f4-45d1-9cfe-7521d373f3d5"
/>



### Known Issues:
- Kicked player gets general message (same when kicked for multi tab)

### Other Issues:
- Host abandoment still existent (host clicks on x; or is closing tab)

## 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
- [x] I have read and accepted the CLA agreement (only required once).

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

[UN]nvm

---------

Co-authored-by: floriankilian <floriankilian@users.noreply.github.com>
2025-08-01 05:01:10 +00:00
Scott Anderson 9b6c290ec7 Merge main into v0.25.x 2025-07-25 18:56:22 -04:00
VariableVince 563d054ea5 Zod v4 update (#1546)
## Description:

This contains the update from zod 3.25 to 4. Change imports back from
zod/v4 to just zod. No errors in VSCode.

## 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
- [x] I have read and accepted the CLA aggreement (only required once).

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

tryout33
2025-07-23 14:28:38 -04:00
Scott Anderson 0489c63f4b Validate spawn tile (#1512)
## Description:

Enforce valid tile during spawn, to prevent the game from crashing for
all players.

## 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
- [ ] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [ ] I have read and accepted the CLA aggreement (only required once).
2025-07-20 20:17:29 -07:00
evanpelle 9fca546745 alert on ws 1002 error (#1472)
## Description:

Alert the error when a user is unauthorized to join a game. Previously
it would just log an error and the user would have no idea why they
couldn't join a game (if for example they were using a restricted
pattern). Now the user gets an alert. We should make this a proper modal
in the future.

## 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
- [x] I have read and accepted the CLA aggreement (only required once).

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

evan
2025-07-17 16:03:01 -07:00
g-santos-m 31381f67f4 Enable @typescript eslint/prefer nullish coalescing eslint rule (#1420)
## Description:

Fixes #952 
Enabled @typescript-eslint/prefer-nullish-coalescing rule and worked
through every error, introducing ?? and ??= operators or disabling
errors with inline comments where appropriate, to the best of my
ability.

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

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

g_santos_m
2025-07-15 03:00:06 +00:00
DevelopingTom 6353a5d6f7 Automatic train stations (#1353)
## Description:

Train stations are now built automatically when a factory is
constructed.

Changes:
- When a factory is built, nearby structures are connected to the rail
network
- When a city is built near a factory, it is connected to the rail
network
    - All structures behave the same when a train stops: to be defined
    - Removed station badge
    - Gold income is now related to the structure's level

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

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

IngloriousTom
2025-07-06 13:15:49 -07:00
evanpelle 5b5ac7bfca allow alliance extension Fixes #491 (#1314)
## Description:

About 30s before an alliance is about to expire, both players receive a
prompt to extend the alliance. If both players agree the alliance is
extended.

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

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

evan
2025-07-02 15:25:20 -07:00
Aotumuri b71acdc993 Patterned territory (#786)
## Description:
This is meant to give players more customization options.
Permission handling hasn’t really been implemented yet.
## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

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

aotumuri
2025-06-22 23:57:24 -04:00
DevelopingTom 43397779fa Add trains (#1159)
## Description:

Add a rail network to handle train stations/railroad between structures.

Changes:
- `RailNetwork` is responsible for the train station graph. Use it to
connect new `TrainStations`
- A `RailRoad` connects two `TrainStation`
- No loop possible in the rail network
- Train stations handles its railroads
- Added a layer to draw the railroads under the structures

#### Clusters
- To speed up computations, each `TrainStation` references its own
cluster
- A cluster is a list of `TrainStation` connected with each other,
created by the `RailNetwork` when connecting the station
- Train stations spawn trains randomly depending on its current cluster
size
- A `TrainStation` decides randomly of the train destination by picking
one from the cluster

#### Production building:
- Added a factory which has no gameplay impact currently. _To be
discussed._

#### Train stops:
- When a train reaches a factory, it's filled with a "cargo". The loaded
trains has no impact currently. _To be discussed._
- When a train reaches a city, the player earn 10k gold
- When a train reaches a port, it sends a new tradeship if possible
- If a destination/source is destroyed, the train & railroad are deleted
too


https://github.com/user-attachments/assets/42375c17-9e04-4a42-98d0-708c81ffd609


https://github.com/user-attachments/assets/fbecdb53-a516-4df8-87fb-1f9a62c4efa0



## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

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

IngloriousTom

---------

Co-authored-by: Scott Anderson <scottanderson@users.noreply.github.com>
2025-06-22 08:14:08 -07:00
Scott Anderson 0f2008a68d Simplify ClientMessage handling (#1235)
## Description:

- Expand the try/catch block in socket message handlers to encapsulate
all possible throwers.
- Remove unnecessary zod schema parsing of outgoing messages.
- Avoid unnecessary serialization and deserialization in singleplayer.

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

---------

Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
2025-06-22 02:34:45 +00:00
evanpelle 9ae544595b fix bad tile crash (#1237)
## Description:

Sending invalid coords can cause game to crash. Make sure to validate
tile ref.

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan
2025-06-20 09:49:55 -07:00
tnhnblgl bd4fe45056 Pot issue fix (#1233)
## Description:
Fixes #1232 
Fixes #1231 

Add handle's 

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:
dovg
2025-06-19 13:27:25 -07:00
Omri 3597f7b326 upgrade to zod 4 (#1161)
## Description:
updates zod to v4
closes #873 

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

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

omrih

---------

Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
2025-06-19 19:34:05 +00:00
evanpelle 4480871f65 have worker send error back to client (#1178)
## Description:
On error, send the message back to the client before closing the
websocket.

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

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

<DISCORD USERNAME>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-06-17 20:13:37 -07:00
Aotumuri e68d48c3a8 Fixed quick chat text injection (#1144)
## Description:
https://github.com/openfrontio/OpenFrontIO/issues/1035
Fixes #1035 
## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

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

.w.

---------

Co-authored-by: Scott Anderson <scottanderson@users.noreply.github.com>
Co-authored-by: evanpelle <evanpelle@gmail.com>
2025-06-13 09:21:22 -07:00