ui/phase-11: map wired to live game state

Replaces the Phase 10 map stub with live planet rendering driven by
`user.games.report`, and wires the header turn counter to the same
data. Phase 11's frontend sits on a per-game `GameStateStore` that
lives in `lib/game-state.svelte.ts`: the in-game shell layout
instantiates one per game, exposes it through Svelte context, and
disposes it on remount. The store discovers the game's current turn
through `lobby.my.games.list`, fetches the matching report, and
exposes a TS-friendly snapshot to the header turn counter, the map
view, and the inspector / order / calculator tabs that later phases
will plug onto the same instance.

The pipeline forced one cross-stage decision: the user surface needs
the current turn number to know which report to fetch, but
`GameSummary` did not expose it. Phase 11 extends the lobby
catalogue (FB schema, transcoder, Go model, backend
gameSummaryWire, gateway decoders, openapi, TS bindings,
api/lobby.ts) with `current_turn:int32`. The data was already
tracked in backend's `RuntimeSnapshot.CurrentTurn`; surfacing it is
a wire change only. Two alternatives were rejected: a brand-new
`user.games.state` message (full wire-flow for one field) and
hard-coding `turn=0` (works for the dev sandbox, which never
advances past zero, but renders the initial state for any real
game). The change crosses Phase 8's already-shipped catalogue per
the project's "decisions baked back into the live plan" rule —
existing tests and fixtures are updated in the same patch.

The state binding lives in `map/state-binding.ts::reportToWorld`:
one Point primitive per planet across all four kinds (local /
other / uninhabited / unidentified) with distinct fill colours,
fill alphas, and point radii so the user can tell them apart at a
glance. The planet engine number is reused as the primitive id so
a hit-test result resolves directly to a planet without an extra
lookup table. Zero-planet reports yield a well-formed empty world;
malformed dimensions fall back to 1×1 so a bad report cannot crash
the renderer.

The map view's mount effect creates the renderer once and skips
re-mount on no-op refreshes (same turn, same wrap mode); a turn
change or wrap-mode flip disposes and recreates it. The renderer's
external API does not yet expose `setWorld`; Phase 24 / 34 will
extract it once high-frequency updates land. The store installs a
`visibilitychange` listener that calls `refresh()` when the tab
regains focus.

Wrap-mode preference uses `Cache` namespace `game-prefs`, key
`<gameId>/wrap-mode`, default `torus`. Phase 11 reads through
`store.wrapMode`; Phase 29 wires the toggle UI on top of
`setWrapMode`.

Tests: Vitest unit coverage for `reportToWorld` (every kind,
ids, styling, empty / zero-dimension edges, priority order) and
for the store lifecycle (init success, missing-membership error,
forbidden-result error, `setTurn`, wrap-mode persistence across
instances, `failBootstrap`). Playwright e2e mocks the gateway for
`lobby.my.games.list` and `user.games.report` and asserts the
live data path: turn counter shows the reported turn,
`active-view-map` flips to `data-status="ready"`, and
`data-planet-count` matches the fixture count. The zero-planet
regression and the missing-membership error path are covered.

Phase 11 status stays `pending` in `ui/PLAN.md` until the local-ci
run lands green; flipping to `done` follows in the next commit per
the per-stage CI gate in `CLAUDE.md`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-08 21:17:17 +02:00
parent ff524fabc6
commit ce7a66b3e6
53 changed files with 5994 additions and 70 deletions
@@ -89,9 +89,12 @@ type gameSummaryWire struct {
EnrollmentEndsAt string `json:"enrollment_ends_at"` EnrollmentEndsAt string `json:"enrollment_ends_at"`
CreatedAt string `json:"created_at"` CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"` UpdatedAt string `json:"updated_at"`
CurrentTurn int32 `json:"current_turn"`
} }
// lobbyGameDetailWire mirrors `LobbyGameDetail` from openapi.yaml. // lobbyGameDetailWire mirrors `LobbyGameDetail` from openapi.yaml.
// `current_turn` is inherited from `gameSummaryWire`; the runtime
// fields below carry the runtime projection on top of it.
type lobbyGameDetailWire struct { type lobbyGameDetailWire struct {
gameSummaryWire gameSummaryWire
Visibility string `json:"visibility"` Visibility string `json:"visibility"`
@@ -100,7 +103,6 @@ type lobbyGameDetailWire struct {
TargetEngineVersion string `json:"target_engine_version"` TargetEngineVersion string `json:"target_engine_version"`
StartGapHours int32 `json:"start_gap_hours"` StartGapHours int32 `json:"start_gap_hours"`
StartGapPlayers int32 `json:"start_gap_players"` StartGapPlayers int32 `json:"start_gap_players"`
CurrentTurn int32 `json:"current_turn"`
RuntimeStatus string `json:"runtime_status"` RuntimeStatus string `json:"runtime_status"`
EngineHealth string `json:"engine_health,omitempty"` EngineHealth string `json:"engine_health,omitempty"`
StartedAt *string `json:"started_at,omitempty"` StartedAt *string `json:"started_at,omitempty"`
@@ -118,6 +120,7 @@ func gameSummaryToWire(g lobby.GameRecord) gameSummaryWire {
EnrollmentEndsAt: g.EnrollmentEndsAt.UTC().Format(timestampLayout), EnrollmentEndsAt: g.EnrollmentEndsAt.UTC().Format(timestampLayout),
CreatedAt: g.CreatedAt.UTC().Format(timestampLayout), CreatedAt: g.CreatedAt.UTC().Format(timestampLayout),
UpdatedAt: g.UpdatedAt.UTC().Format(timestampLayout), UpdatedAt: g.UpdatedAt.UTC().Format(timestampLayout),
CurrentTurn: g.RuntimeSnapshot.CurrentTurn,
} }
if g.OwnerUserID != nil { if g.OwnerUserID != nil {
s := g.OwnerUserID.String() s := g.OwnerUserID.String()
@@ -135,7 +138,6 @@ func lobbyGameDetailToWire(g lobby.GameRecord) lobbyGameDetailWire {
TargetEngineVersion: g.TargetEngineVersion, TargetEngineVersion: g.TargetEngineVersion,
StartGapHours: g.StartGapHours, StartGapHours: g.StartGapHours,
StartGapPlayers: g.StartGapPlayers, StartGapPlayers: g.StartGapPlayers,
CurrentTurn: g.RuntimeSnapshot.CurrentTurn,
RuntimeStatus: g.RuntimeSnapshot.RuntimeStatus, RuntimeStatus: g.RuntimeSnapshot.RuntimeStatus,
EngineHealth: g.RuntimeSnapshot.EngineHealth, EngineHealth: g.RuntimeSnapshot.EngineHealth,
} }
+8 -3
View File
@@ -2515,6 +2515,7 @@ components:
- enrollment_ends_at - enrollment_ends_at
- created_at - created_at
- updated_at - updated_at
- current_turn
properties: properties:
game_id: game_id:
type: string type: string
@@ -2563,6 +2564,13 @@ components:
updated_at: updated_at:
type: string type: string
format: date-time format: date-time
current_turn:
type: integer
description: |
Most recent turn number observed by backend's runtime
projection. Zero before the engine produces its first
snapshot. The user surface uses it to fetch the matching
`user.games.report` without a separate state query.
GameSummaryPage: GameSummaryPage:
type: object type: object
additionalProperties: false additionalProperties: false
@@ -2720,7 +2728,6 @@ components:
- target_engine_version - target_engine_version
- start_gap_hours - start_gap_hours
- start_gap_players - start_gap_players
- current_turn
- runtime_status - runtime_status
properties: properties:
visibility: visibility:
@@ -2736,8 +2743,6 @@ components:
type: integer type: integer
start_gap_players: start_gap_players:
type: integer type: integer
current_turn:
type: integer
runtime_status: runtime_status:
type: string type: string
engine_health: engine_health:
@@ -390,6 +390,7 @@ func decodeGameSummaryFromGameDetail(payload []byte) (lobbymodel.GameSummary, er
EnrollmentEndsAt time.Time `json:"enrollment_ends_at"` EnrollmentEndsAt time.Time `json:"enrollment_ends_at"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
CurrentTurn int32 `json:"current_turn"`
} }
if err := json.Unmarshal(payload, &wire); err != nil { if err := json.Unmarshal(payload, &wire); err != nil {
return lobbymodel.GameSummary{}, fmt.Errorf("decode success response: %w", err) return lobbymodel.GameSummary{}, fmt.Errorf("decode success response: %w", err)
@@ -409,6 +410,7 @@ func decodeGameSummaryFromGameDetail(payload []byte) (lobbymodel.GameSummary, er
EnrollmentEndsAt: wire.EnrollmentEndsAt.UTC(), EnrollmentEndsAt: wire.EnrollmentEndsAt.UTC(),
CreatedAt: wire.CreatedAt.UTC(), CreatedAt: wire.CreatedAt.UTC(),
UpdatedAt: wire.UpdatedAt.UTC(), UpdatedAt: wire.UpdatedAt.UTC(),
CurrentTurn: wire.CurrentTurn,
}, nil }, nil
} }
@@ -425,6 +427,7 @@ func decodePublicGamesPage(payload []byte) (*lobbymodel.PublicGamesListResponse,
EnrollmentEndsAt time.Time `json:"enrollment_ends_at"` EnrollmentEndsAt time.Time `json:"enrollment_ends_at"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
CurrentTurn int32 `json:"current_turn"`
} `json:"items"` } `json:"items"`
Page int `json:"page"` Page int `json:"page"`
PageSize int `json:"page_size"` PageSize int `json:"page_size"`
@@ -455,6 +458,7 @@ func decodePublicGamesPage(payload []byte) (*lobbymodel.PublicGamesListResponse,
EnrollmentEndsAt: w.EnrollmentEndsAt.UTC(), EnrollmentEndsAt: w.EnrollmentEndsAt.UTC(),
CreatedAt: w.CreatedAt.UTC(), CreatedAt: w.CreatedAt.UTC(),
UpdatedAt: w.UpdatedAt.UTC(), UpdatedAt: w.UpdatedAt.UTC(),
CurrentTurn: w.CurrentTurn,
}) })
} }
return out, nil return out, nil
+5 -1
View File
@@ -62,7 +62,10 @@ type MyGamesListResponse struct {
// GameSummary stores one game record returned by the various lobby // GameSummary stores one game record returned by the various lobby
// list endpoints. `OwnerUserID` is empty for public games (no human // list endpoints. `OwnerUserID` is empty for public games (no human
// owner). // owner). `CurrentTurn` carries the runtime's most recently observed
// turn number; the value is zero before the engine produces its first
// snapshot. The user surface uses it to fetch the corresponding
// `user.games.report` without an extra round-trip.
type GameSummary struct { type GameSummary struct {
GameID string `json:"game_id"` GameID string `json:"game_id"`
GameName string `json:"game_name"` GameName string `json:"game_name"`
@@ -74,6 +77,7 @@ type GameSummary struct {
EnrollmentEndsAt time.Time `json:"enrollment_ends_at"` EnrollmentEndsAt time.Time `json:"enrollment_ends_at"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
CurrentTurn int32 `json:"current_turn"`
} }
// PublicGamesListRequest stores the paginated read request for joinable // PublicGamesListRequest stores the paginated read request for joinable
+6 -1
View File
@@ -6,7 +6,11 @@ namespace lobby;
// GameSummary stores one game record returned by the lobby list // GameSummary stores one game record returned by the lobby list
// endpoints. owner_user_id is empty for public games (no human owner). // endpoints. owner_user_id is empty for public games (no human owner).
// The shape matches `lobby/openapi.yaml` `MyGameSummary`. // current_turn carries the runtime's most recent observed turn number
// (zero before the engine produces its first snapshot); the user
// surface uses it to read the corresponding `user.games.report`
// without an extra round-trip. The shape matches `lobby/openapi.yaml`
// `MyGameSummary`.
table GameSummary { table GameSummary {
game_id:string; game_id:string;
game_name:string; game_name:string;
@@ -18,6 +22,7 @@ table GameSummary {
enrollment_ends_at_ms:int64; enrollment_ends_at_ms:int64;
created_at_ms:int64; created_at_ms:int64;
updated_at_ms:int64; updated_at_ms:int64;
current_turn:int32;
} }
// MyGamesListRequest stores the authenticated read request for the // MyGamesListRequest stores the authenticated read request for the
+16 -1
View File
@@ -141,8 +141,20 @@ func (rcv *GameSummary) MutateUpdatedAtMs(n int64) bool {
return rcv._tab.MutateInt64Slot(22, n) return rcv._tab.MutateInt64Slot(22, n)
} }
func (rcv *GameSummary) CurrentTurn() int32 {
o := flatbuffers.UOffsetT(rcv._tab.Offset(24))
if o != 0 {
return rcv._tab.GetInt32(o + rcv._tab.Pos)
}
return 0
}
func (rcv *GameSummary) MutateCurrentTurn(n int32) bool {
return rcv._tab.MutateInt32Slot(24, n)
}
func GameSummaryStart(builder *flatbuffers.Builder) { func GameSummaryStart(builder *flatbuffers.Builder) {
builder.StartObject(10) builder.StartObject(11)
} }
func GameSummaryAddGameId(builder *flatbuffers.Builder, gameId flatbuffers.UOffsetT) { func GameSummaryAddGameId(builder *flatbuffers.Builder, gameId flatbuffers.UOffsetT) {
builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(gameId), 0) builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(gameId), 0)
@@ -174,6 +186,9 @@ func GameSummaryAddCreatedAtMs(builder *flatbuffers.Builder, createdAtMs int64)
func GameSummaryAddUpdatedAtMs(builder *flatbuffers.Builder, updatedAtMs int64) { func GameSummaryAddUpdatedAtMs(builder *flatbuffers.Builder, updatedAtMs int64) {
builder.PrependInt64Slot(9, updatedAtMs, 0) builder.PrependInt64Slot(9, updatedAtMs, 0)
} }
func GameSummaryAddCurrentTurn(builder *flatbuffers.Builder, currentTurn int32) {
builder.PrependInt32Slot(10, currentTurn, 0)
}
func GameSummaryEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { func GameSummaryEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
return builder.EndObject() return builder.EndObject()
} }
+2
View File
@@ -783,6 +783,7 @@ func encodeGameSummary(builder *flatbuffers.Builder, summary lobbymodel.GameSumm
lobbyfbs.GameSummaryAddEnrollmentEndsAtMs(builder, summary.EnrollmentEndsAt.UTC().UnixMilli()) lobbyfbs.GameSummaryAddEnrollmentEndsAtMs(builder, summary.EnrollmentEndsAt.UTC().UnixMilli())
lobbyfbs.GameSummaryAddCreatedAtMs(builder, summary.CreatedAt.UTC().UnixMilli()) lobbyfbs.GameSummaryAddCreatedAtMs(builder, summary.CreatedAt.UTC().UnixMilli())
lobbyfbs.GameSummaryAddUpdatedAtMs(builder, summary.UpdatedAt.UTC().UnixMilli()) lobbyfbs.GameSummaryAddUpdatedAtMs(builder, summary.UpdatedAt.UTC().UnixMilli())
lobbyfbs.GameSummaryAddCurrentTurn(builder, summary.CurrentTurn)
return lobbyfbs.GameSummaryEnd(builder) return lobbyfbs.GameSummaryEnd(builder)
} }
@@ -798,6 +799,7 @@ func decodeGameSummary(summary *lobbyfbs.GameSummary) lobbymodel.GameSummary {
EnrollmentEndsAt: time.UnixMilli(summary.EnrollmentEndsAtMs()).UTC(), EnrollmentEndsAt: time.UnixMilli(summary.EnrollmentEndsAtMs()).UTC(),
CreatedAt: time.UnixMilli(summary.CreatedAtMs()).UTC(), CreatedAt: time.UnixMilli(summary.CreatedAtMs()).UTC(),
UpdatedAt: time.UnixMilli(summary.UpdatedAtMs()).UTC(), UpdatedAt: time.UnixMilli(summary.UpdatedAtMs()).UTC(),
CurrentTurn: summary.CurrentTurn(),
} }
} }
+3 -1
View File
@@ -29,13 +29,14 @@ func TestLobbyMyGamesListRoundTrip(t *testing.T) {
GameID: "game-private-7c8f", GameID: "game-private-7c8f",
GameName: "First Contact", GameName: "First Contact",
GameType: "private", GameType: "private",
Status: "draft", Status: "running",
OwnerUserID: "user-9912", OwnerUserID: "user-9912",
MinPlayers: 2, MinPlayers: 2,
MaxPlayers: 8, MaxPlayers: 8,
EnrollmentEndsAt: ends, EnrollmentEndsAt: ends,
CreatedAt: created, CreatedAt: created,
UpdatedAt: updated, UpdatedAt: updated,
CurrentTurn: 7,
}, },
{ {
GameID: "game-public-aabb", GameID: "game-public-aabb",
@@ -48,6 +49,7 @@ func TestLobbyMyGamesListRoundTrip(t *testing.T) {
EnrollmentEndsAt: ends, EnrollmentEndsAt: ends,
CreatedAt: created, CreatedAt: created,
UpdatedAt: updated, UpdatedAt: updated,
CurrentTurn: 0,
}, },
}, },
} }
+1 -1
View File
@@ -6,7 +6,7 @@ WASM_OUT := frontend/static/core.wasm
WASM_EXEC := frontend/static/wasm_exec.js WASM_EXEC := frontend/static/wasm_exec.js
TINYGO_ROOT := $(shell tinygo env TINYGOROOT 2>/dev/null) TINYGO_ROOT := $(shell tinygo env TINYGOROOT 2>/dev/null)
FBS_OUT := frontend/src/proto/galaxy/fbs FBS_OUT := frontend/src/proto/galaxy/fbs
FBS_INPUTS := ../pkg/schema/fbs/lobby.fbs ../pkg/schema/fbs/user.fbs FBS_INPUTS := ../pkg/schema/fbs/common.fbs ../pkg/schema/fbs/lobby.fbs ../pkg/schema/fbs/user.fbs ../pkg/schema/fbs/report.fbs
help: help:
@echo "ui targets:" @echo "ui targets:"
+143 -19
View File
@@ -1165,22 +1165,140 @@ Status: pending.
Goal: replace the map fixture with real planet data fetched from the Goal: replace the map fixture with real planet data fetched from the
gateway for the selected game; planets only, read-only. gateway for the selected game; planets only, read-only.
Artifacts: Decisions taken with the project owner during implementation:
- `ui/frontend/src/api/game-state.ts` fetch latest game state via 1. **`current_turn` on `GameSummary`.** The user-facing
`user.games.report` `lobby.my.games.list` did not expose the runtime's current turn
- `ui/frontend/src/map/state-binding.ts` map-state synchroniser number, but the in-game shell needs it to fetch the matching
applying planets to the renderer `user.games.report`. Phase 11 extends `GameSummary` with a new
- `ui/frontend/src/lib/active-view/map.svelte` integrates the renderer `current_turn:int32` field (FB schema, Go transcoder + model,
with live data and a loading state, defaulting to torus mode and backend `gameSummaryWire`, gateway `decodeGameSummary*`,
reading the per-game wrap-scrolling preference from `Cache` (toggle `backend/openapi.yaml`, TS bindings, `api/lobby.ts`). The data
itself is exposed in Phase 29) was already tracked in the runtime projection
- `ui/frontend/src/lib/header/turn-counter.svelte` reads the live (`backend/internal/lobby/types.go RuntimeSnapshot.CurrentTurn`);
turn number from game state exposing it is purely a wire change. Two alternatives were
rejected: a brand-new `user.games.state` message (full wire-flow
for a one-field response) and hard-coding `turn=0` (works for the
dev sandbox, but renders the initial state for any game past
turn zero). The decision crosses Phase 8's already-shipped
catalogue per the project's "decisions baked back into the live
plan" rule.
2. **Per-game state store with context.** A `GameStateStore` lives
in `lib/game-state.svelte.ts`; the in-game shell layout
instantiates one per game and exposes it through Svelte context
under `GAME_STATE_CONTEXT_KEY`. Header turn counter, map view,
and (in later phases) inspector tabs all consume the same
instance. A new instance is created on layout remount (game id
change), so each game gets a fresh snapshot.
3. **Lobby lookup for current turn.** The store does not assume the
caller passed `current_turn` through navigation state. On
`setGame`, it calls `lobby.my.games.list` itself, finds the game
record, reads `current_turn`, and then calls
`user.games.report`. A direct deep link to `/games/:id/map` for
a game the user is not a member of flips the store to `error`
with a `not in your list` message.
4. **Refresh on tab focus.** The store installs a
`visibilitychange` listener that calls `refresh()` when the
document becomes visible and the store is `ready`. The map
view's mount effect skips a re-render when the new snapshot's
turn matches the previously-mounted turn (and the wrap mode is
unchanged), so a no-op refresh does not flicker the canvas.
5. **Wrap-mode preference.** `Cache` namespace `game-prefs`, key
`<gameId>/wrap-mode`, values `torus` (default) / `no-wrap`.
Phase 11 reads through `wrapMode`; `setWrapMode` writes back.
Phase 29 wires the toggle UI on top of these primitives.
6. **State binding.** `map/state-binding.ts::reportToWorld` emits
one Point primitive per planet across all four kinds (local /
other / uninhabited / unidentified) with distinct fill colours
and point radii. Each primitive's id reuses the engine planet
number so a hit-test result resolves directly to a planet
without an extra lookup table. Zero-planet reports yield a
well-formed empty world; the World constructor's positivity
check is guarded by a 1×1 fallback for the malformed-report
edge case.
7. **Renderer remount on snapshot change.** The map view disposes
and recreates the renderer when the report's turn changes (and
short-circuits when it does not). This is wasteful for the
tab-focus refresh path, but the renderer's external
`RendererHandle` does not yet expose a `setWorld` API and Phase
11's per-game planet count is small enough that the remount
cost (a few hundred ms) is acceptable. A future phase that adds
high-frequency updates (Phase 24 push events, Phase 34 multi-
turn projection overlays) will extract a `replaceWorld` method.
8. **e2e bootstrap reuses `__galaxyDebug`.** The Phase 10 pattern
of seeding the device session through `/__debug/store` carries
over; the gateway is mocked through `page.route` for
`lobby.my.games.list`, `user.games.report`, and the
`SubscribeEvents` stream that the revocation watcher opens
(held open indefinitely so a clean end-of-stream does not
trigger `signOut("revoked")` and bounce the test back to
`/login`).
Artifacts (delivered):
- `ui/frontend/src/api/game-state.ts` — typed wrapper for
`user.games.report` plus `uuidToHiLo` and a TS-friendly
`GameReport` shape (planets only)
- `ui/frontend/src/lib/game-state.svelte.ts` — runes-based
`GameStateStore` with init / setGame / setTurn / refresh /
setWrapMode / failBootstrap / dispose; tab-focus listener;
`Cache`-backed wrap-mode persistence
- `ui/frontend/src/map/state-binding.ts` — `reportToWorld` and the
per-kind planet styling
- `ui/frontend/src/lib/active-view/map.svelte` — replaces the
Phase 10 stub with the live renderer integration plus loading /
error overlays and a `data-planet-count` testid hook
- `ui/frontend/src/lib/header/turn-counter.svelte` — reads
`store.report.turn` through context, falls back to the static
`?` placeholder when the store has not yet produced a snapshot
- `ui/frontend/src/routes/games/[id]/+layout.svelte` — instantiates
the `GameStateStore`, builds the `GalaxyClient`, exposes the
store via `setContext`, disposes on unmount
- `pkg/schema/fbs/lobby.fbs` — `current_turn:int32` field
- `pkg/schema/fbs/lobby/GameSummary.go` (regenerated)
- `pkg/transcoder/lobby.go` — encode/decode `current_turn`
- `pkg/transcoder/lobby_test.go` — non-zero `current_turn` in the
round-trip fixture
- `pkg/model/lobby/lobby.go` — `CurrentTurn int32` on `GameSummary`
- `backend/internal/server/handlers_user_lobby_helpers.go` —
`gameSummaryWire.CurrentTurn` + `gameSummaryToWire` reads it
from `RuntimeSnapshot.CurrentTurn`; `lobbyGameDetailWire` no
longer redeclares the field
- `backend/openapi.yaml` — `current_turn` on the `GameSummary`
schema (required); removed from the `LobbyGameDetail` allOf
block (now inherited)
- `gateway/internal/backendclient/lobby_commands.go` —
`decodeGameSummaryFromGameDetail` and `decodePublicGamesPage`
parse `current_turn` from JSON
- `ui/Makefile` — `FBS_INPUTS` adds `common.fbs` (so the
`common/uuid.ts` directory is generated) and `report.fbs`
- `ui/frontend/src/proto/galaxy/fbs/{common,report}/...` —
regenerated TS bindings
- `ui/frontend/src/api/lobby.ts` — `currentTurn: number` on
`GameSummary`; `decodeGameSummary` reads it
- `ui/frontend/tests/lobby-{fbs,api,page}.test.ts` and
`tests/e2e/fixtures/lobby-fbs.ts` — fixtures and assertions
cover `currentTurn`
- `ui/frontend/tests/state-binding.test.ts` — Vitest unit
coverage for `reportToWorld` (dimensions, kinds, ids, styling,
empty-planet, zero-dimension fallback, priority order)
- `ui/frontend/tests/game-state.test.ts` — Vitest coverage for
`GameStateStore` (init flow, missing-membership error,
forbidden-result error, `setTurn`, wrap-mode persistence
across instances, `failBootstrap`)
- `ui/frontend/tests/e2e/game-shell-map.spec.ts` — Playwright e2e
with a mocked gateway: live report renders the reported turn
and planet count, zero-planet game renders without errors,
missing-membership game surfaces the error overlay
- `ui/frontend/tests/e2e/fixtures/report-fbs.ts` — `buildReportPayload`
helper for forging FB Report payloads
- Topic doc `ui/docs/game-state.md`
- `ui/docs/lobby.md` — `current_turn` note pointing at the new
game-state doc
Dependencies: Phases 9, 10. Dependencies: Phases 9, 10.
Acceptance criteria: Acceptance criteria (met):
- entering `/games/:id/map` for a game with real planets renders them - entering `/games/:id/map` for a game with real planets renders them
on the map; on the map;
@@ -1189,14 +1307,20 @@ Acceptance criteria:
- view mode (torus / no-wrap) honours the per-game preference if set, - view mode (torus / no-wrap) honours the per-game preference if set,
defaults to torus otherwise. defaults to torus otherwise.
Targeted tests: Targeted tests (delivered):
- Vitest unit tests for `state-binding.ts` translating report data to - Vitest: `tests/state-binding.test.ts` covers the report→world
primitives; translation across every planet kind plus malformed-dimension
- Playwright e2e against a local stack with seeded game state; guards; `tests/game-state.test.ts` covers the store lifecycle
- regression test: zero-planet game shows the map empty without errors; end-to-end with a stubbed `listMyGames` and a fake `GalaxyClient`;
- regression test: per-game wrap-scrolling preference persists and is - Playwright e2e: `tests/e2e/game-shell-map.spec.ts` exercises the
applied on next visit to the game. live data path with a mocked gateway across all four projects,
including the zero-planet regression and the
missing-membership error path;
- per-game wrap-scrolling preference round-trips through `Cache`
in `game-state.test.ts::setWrapMode persists across instances`;
- the existing Phase 10 chrome / navigation specs still pass
unchanged.
## Phase 12. Order Composer Skeleton ## Phase 12. Order Composer Skeleton
+104
View File
@@ -0,0 +1,104 @@
# Per-game state store
This document describes the per-game state owned by the in-game shell
layout. Phase 11 introduces the store and uses it for two consumers
(the header turn counter and the map view); later phases plug
inspector tabs, the order composer, and the calculator on top of the
same instance.
## Lifecycle
`routes/games/[id]/+layout.svelte` instantiates one `GameStateStore`
per game (the layout remounts when the user navigates to a different
game id, so each game gets a fresh store). The layout exposes the
instance through Svelte context under `GAME_STATE_CONTEXT_KEY`;
descendants read it via `getContext(GAME_STATE_CONTEXT_KEY)`.
The layout's `onMount` builds the `GalaxyClient`, loads `Cache`
through `loadStore()`, then calls `gameState.init({ client, cache,
gameId })`. `init`:
1. installs a `visibilitychange` listener on `document` so the report
is refreshed when the tab regains focus;
2. calls `setGame(gameId)`, which:
- reads the per-game wrap-mode preference from `Cache`
(`game-prefs / <gameId>/wrap-mode`, default `torus`);
- calls `lobby.my.games.list` and finds the game record (the
Phase 11 wire schema extension on `GameSummary` adds
`current_turn`); if the user is not a member, the store flips
to `error`;
- calls `user.games.report` for the discovered turn and decodes
the FlatBuffers response into a TS-friendly `GameReport` shape.
The store exposes:
| field | type | meaning |
| ------------- | ----------------------------- | ---------------------------------------------------- |
| `gameId` | `string` | active game id |
| `status` | `idle / loading / ready / error` | current lifecycle state |
| `report` | `GameReport \| null` | latest decoded report, `null` until first fetch |
| `wrapMode` | `torus / no-wrap` | per-game preference, persisted via `Cache` |
| `error` | `string \| null` | localised error message when `status === "error"` |
## Phase boundaries
- Phase 11 surfaces only the planet subset of the report. Later
phases extend `GameReport` and `decodeReport` as their slice of
the wire lands (ships, fleets, sciences, routes, battles, mail).
- Phase 26 wires history mode through `setTurn(turn)`. The store
already supports it; the navigator UI is what is missing.
- Phase 24 replaces the tab-focus refresh with push-event-driven
refreshes; the visibility listener stays as a fallback for
background tabs that miss a push.
- Phase 29 wires the wrap-mode toggle UI on top of `setWrapMode`.
## Why `current_turn` lives on `GameSummary`
The user-facing surface needs the current turn number to know which
report to fetch. Two alternatives were rejected:
- a brand-new `user.games.state` message — adds a full wire-flow
(fbs schema, transcoder, gateway routing, backend handler) for a
one-field response;
- hard-coding `turn=0` for all games — works for the dev sandbox
(which never advances past turn zero) but renders the initial
state for any real game past turn zero.
Extending `GameSummary` reuses the existing lobby pipeline; the
backend already tracks `current_turn` in its runtime projection
(`backend/internal/server/handlers_user_lobby_helpers.go`
`gameSummaryToWire` reads it from `g.RuntimeSnapshot.CurrentTurn`).
The wire change touches Phase 8's already-shipped catalogue, but the
`current_turn` field defaults to zero on the FB side, so existing
tests and the dev sandbox flow continue to work unchanged.
## State binding
`map/state-binding.ts::reportToWorld(report)` translates a
`GameReport` into a renderer-ready `World`. Phase 11 emits one Point
primitive per planet across all four kinds (local / other /
uninhabited / unidentified). Each kind gets a distinct fill colour,
fill alpha, and point radius so the four classes are
visually-distinguishable at a glance; later phases will refine the
colour palette as the visual language stabilises (Phase 35 polish).
The planet engine number is reused as the primitive id so a hit-test
result can resolve back to a planet without an extra lookup table.
## Refresh discipline
`refresh()` re-fetches the same turn snapshot. It is called by the
`visibilitychange` handler when `document.visibilityState ===
"visible"` and the store is already in `ready` state. The map view's
mount effect skips a re-render when the new snapshot's turn matches
the previously-mounted turn (and the wrap mode is unchanged), so a
no-op refresh does not flicker the canvas.
`setTurn(turn)` is the entry point for Phase 26 history mode:
calling it on a different turn loads that snapshot and the same
mount effect re-creates the renderer with the new world.
`setWrapMode(mode)` writes to `Cache` and updates the rune; the
map view's effect picks the change up and re-mounts the renderer
with the new mode.
+7 -1
View File
@@ -18,7 +18,7 @@ width.
| Section | Empty state | Source | Action | | Section | Empty state | Source | Action |
| -------------------- | --------------------- | -------------------------- | --------------------------------------------------------- | | -------------------- | --------------------- | -------------------------- | --------------------------------------------------------- |
| `create new game` | (always visible) | — | Navigates to `/lobby/create` | | `create new game` | (always visible) | — | Navigates to `/lobby/create` |
| `my games` | `no games yet` | `lobby.my.games.list` | Click → `/games/:id/map` (placeholder until Phase 10) | | `my games` | `no games yet` | `lobby.my.games.list` | Click → `/games/:id/map` |
| `pending invitations`| `no invitations` | `lobby.my.invites.list` | Accept (`lobby.invite.redeem`) / Decline (`lobby.invite.decline`) | | `pending invitations`| `no invitations` | `lobby.my.invites.list` | Accept (`lobby.invite.redeem`) / Decline (`lobby.invite.decline`) |
| `my applications` | `no applications` | `lobby.my.applications.list` | Status badge (`pending` / `approved` / `rejected`) | | `my applications` | `no applications` | `lobby.my.applications.list` | Status badge (`pending` / `approved` / `rejected`) |
| `public games` | `no public games` | `lobby.public.games.list` | Submit application via inline race-name form (`lobby.application.submit`) | | `public games` | `no public games` | `lobby.public.games.list` | Submit application via inline race-name form (`lobby.application.submit`) |
@@ -27,6 +27,12 @@ The header preserves the device-session-id `<code>` block from the
Phase 7 placeholder (kept as a debug affordance) plus a greeting if Phase 7 placeholder (kept as a debug affordance) plus a greeting if
the gateway returns a `display_name` for the caller. the gateway returns a `display_name` for the caller.
`GameSummary` carries an extra `current_turn` field (Phase 11
extension) that the lobby UI does not display directly — the in-game
shell reads it from the same payload to load the matching
`user.games.report` for the map view without an additional gateway
call. See [`game-state.md`](game-state.md) for the consumer's view.
## Application lifecycle ## Application lifecycle
`Submit application` on a public-game card toggles an inline race-name `Submit application` on a public-game card toggles an inline race-name
+178
View File
@@ -0,0 +1,178 @@
// Typed wrapper around `GalaxyClient.executeCommand("user.games.report",
// ...)`. The signed-gRPC wire shape is the FlatBuffers
// `report.GameReportRequest` for the request and `report.Report` for
// the response (see `pkg/schema/fbs/report.fbs`). Phase 11 only
// surfaces the planet subset of the response — full ship / fleet /
// science decoding lands in Phases 17-22.
import { Builder, ByteBuffer } from "flatbuffers";
import type { GalaxyClient } from "./galaxy-client";
import { UUID } from "../proto/galaxy/fbs/common";
import {
GameReportRequest,
Report,
} from "../proto/galaxy/fbs/report";
const MESSAGE_TYPE = "user.games.report";
export class GameStateError extends Error {
readonly resultCode: string;
readonly code: string;
constructor(resultCode: string, code: string, message: string) {
super(message);
this.name = "GameStateError";
this.resultCode = resultCode;
this.code = code;
}
}
export interface ReportPlanet {
number: number;
name: string;
x: number;
y: number;
kind: "local" | "other" | "uninhabited" | "unidentified";
owner: string | null;
size: number | null;
resources: number | null;
}
export interface GameReport {
turn: number;
mapWidth: number;
mapHeight: number;
planetCount: number;
planets: ReportPlanet[];
}
export async function fetchGameReport(
client: GalaxyClient,
gameId: string,
turn: number,
): Promise<GameReport> {
const builder = new Builder(64);
const [hi, lo] = uuidToHiLo(gameId);
const gameIdOffset = UUID.createUUID(builder, hi, lo);
GameReportRequest.startGameReportRequest(builder);
GameReportRequest.addGameId(builder, gameIdOffset);
GameReportRequest.addTurn(builder, turn);
builder.finish(GameReportRequest.endGameReportRequest(builder));
const result = await client.executeCommand(MESSAGE_TYPE, builder.asUint8Array());
if (result.resultCode !== "ok") {
const { code, message } = decodeErrorMessage(result.payloadBytes);
throw new GameStateError(result.resultCode, code, message);
}
const buffer = new ByteBuffer(result.payloadBytes);
const report = Report.getRootAsReport(buffer);
return decodeReport(report);
}
function decodeReport(report: Report): GameReport {
const planets: ReportPlanet[] = [];
for (let i = 0; i < report.localPlanetLength(); i++) {
const p = report.localPlanet(i);
if (p === null) continue;
planets.push({
number: Number(p.number()),
name: p.name() ?? "",
x: p.x(),
y: p.y(),
kind: "local",
owner: null,
size: p.size(),
resources: p.resources(),
});
}
for (let i = 0; i < report.otherPlanetLength(); i++) {
const p = report.otherPlanet(i);
if (p === null) continue;
planets.push({
number: Number(p.number()),
name: p.name() ?? "",
x: p.x(),
y: p.y(),
kind: "other",
owner: p.owner() ?? null,
size: p.size(),
resources: p.resources(),
});
}
for (let i = 0; i < report.uninhabitedPlanetLength(); i++) {
const p = report.uninhabitedPlanet(i);
if (p === null) continue;
planets.push({
number: Number(p.number()),
name: p.name() ?? "",
x: p.x(),
y: p.y(),
kind: "uninhabited",
owner: null,
size: p.size(),
resources: p.resources(),
});
}
for (let i = 0; i < report.unidentifiedPlanetLength(); i++) {
const p = report.unidentifiedPlanet(i);
if (p === null) continue;
planets.push({
number: Number(p.number()),
name: "",
x: p.x(),
y: p.y(),
kind: "unidentified",
owner: null,
size: null,
resources: null,
});
}
return {
turn: Number(report.turn()),
mapWidth: report.width(),
mapHeight: report.height(),
planetCount: report.planetCount(),
planets,
};
}
/**
* uuidToHiLo splits the canonical 36-character UUID string
* (`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`) into the two big-endian
* uint64 halves used by `common.UUID`. Mirrors `pkg/transcoder/uuid.go`.
*/
export function uuidToHiLo(value: string): [bigint, bigint] {
const hex = value.replace(/-/g, "").toLowerCase();
if (hex.length !== 32 || /[^0-9a-f]/.test(hex)) {
throw new GameStateError(
"invalid_request",
"invalid_request",
`invalid uuid: ${value}`,
);
}
const hi = BigInt(`0x${hex.slice(0, 16)}`);
const lo = BigInt(`0x${hex.slice(16, 32)}`);
return [hi, lo];
}
function decodeErrorMessage(payload: Uint8Array): { code: string; message: string } {
if (payload.length === 0) {
return { code: "internal_error", message: "empty error payload" };
}
try {
const text = new TextDecoder().decode(payload);
const parsed = JSON.parse(text) as { code?: string; message?: string };
return {
code: typeof parsed.code === "string" ? parsed.code : "internal_error",
message: typeof parsed.message === "string" ? parsed.message : text,
};
} catch {
return { code: "internal_error", message: "non-json error payload" };
}
}
+8
View File
@@ -55,6 +55,13 @@ export interface GameSummary {
enrollmentEndsAt: Date; enrollmentEndsAt: Date;
createdAt: Date; createdAt: Date;
updatedAt: Date; updatedAt: Date;
/**
* Most recent turn number observed by backend's runtime
* projection. Zero before the engine produces its first
* snapshot. The map view uses this to fetch the matching
* `user.games.report` without a separate state query.
*/
currentTurn: number;
} }
export interface PublicGamesPage { export interface PublicGamesPage {
@@ -319,6 +326,7 @@ function decodeGameSummary(summary: FbsGameSummary): GameSummary {
enrollmentEndsAt: dateFromMs(summary.enrollmentEndsAtMs()), enrollmentEndsAt: dateFromMs(summary.enrollmentEndsAtMs()),
createdAt: dateFromMs(summary.createdAtMs()), createdAt: dateFromMs(summary.createdAtMs()),
updatedAt: dateFromMs(summary.updatedAtMs()), updatedAt: dateFromMs(summary.updatedAtMs()),
currentTurn: summary.currentTurn(),
}; };
} }
+173 -15
View File
@@ -1,29 +1,187 @@
<!-- <!--
Phase 10 stub for the map active view. Phase 11 swaps this for the Phase 11 map active view: integrates the Phase 9 renderer with the
live renderer integration described in `ui/PLAN.md` Phase 11. The per-game `GameStateStore` provided through context by
stub keeps the same `data-testid` so Phase 11's spec replaces the `routes/games/[id]/+layout.svelte`. The view mounts the renderer
copy assertion without touching navigation. once the store has produced a report and re-mounts when the
report's turn changes (a refresh that returns the same turn keeps
the existing renderer instance alive). Empty-planet reports render
the empty world without errors — the regression test in
`tests/e2e/game-shell-map.spec.ts` covers this.
Phase 9 owns the renderer's hit-test and pan/zoom semantics; Phase 13
will plug map clicks into the inspector. Phase 29 wires the wrap-mode
toggle on top of the per-game `wrapMode` preference the store
already manages.
--> -->
<script lang="ts"> <script lang="ts">
import { getContext, onDestroy, onMount, untrack } from "svelte";
import { i18n } from "$lib/i18n/index.svelte"; import { i18n } from "$lib/i18n/index.svelte";
import {
createRenderer,
minScaleNoWrap,
type RendererHandle,
} from "../../map/index";
import { reportToWorld } from "../../map/state-binding";
import {
GAME_STATE_CONTEXT_KEY,
type GameStateStore,
} from "$lib/game-state.svelte";
const store = getContext<GameStateStore | undefined>(GAME_STATE_CONTEXT_KEY);
let canvasEl: HTMLCanvasElement | null = $state(null);
let containerEl: HTMLDivElement | null = $state(null);
let mountError: string | null = $state(null);
let handle: RendererHandle | null = null;
let mountedTurn: number | null = null;
let mountedGameId: string | null = null;
let onResize: (() => void) | null = null;
let mounted = false;
$effect(() => {
const report = store?.report;
const status = store?.status ?? "idle";
// Track the wrap mode so the renderer remounts when Phase 29's
// toggle UI flips it; the read here also subscribes the effect.
const mode = store?.wrapMode ?? "torus";
const gameId = store?.gameId ?? "";
if (!mounted || canvasEl === null || containerEl === null) return;
if (status !== "ready" || !report) return;
// Skip a re-mount when the same turn is reloaded for the same
// game and the wrap mode did not change. The store's `refresh`
// path lands here on tab focus; an unchanged snapshot must not
// flicker the canvas.
const sameSnapshot =
mountedTurn === report.turn &&
mountedGameId === gameId &&
handle !== null &&
handle.getMode() === mode;
if (sameSnapshot) return;
untrack(() => {
void mountRenderer(report, mode);
});
});
async function mountRenderer(
report: NonNullable<GameStateStore["report"]>,
mode: "torus" | "no-wrap",
): Promise<void> {
if (canvasEl === null || containerEl === null) return;
if (handle !== null) {
handle.dispose();
handle = null;
}
try {
const world = reportToWorld(report);
handle = await createRenderer({
canvas: canvasEl,
world,
mode,
preference: ["webgpu", "webgl"],
});
handle.viewport.moveCenter(world.width / 2, world.height / 2);
const minScale = minScaleNoWrap(
{
widthPx: containerEl.clientWidth,
heightPx: containerEl.clientHeight,
},
world,
);
handle.viewport.setZoom(minScale * 1.05, true);
if (mode === "no-wrap") handle.setMode("no-wrap");
mountedTurn = report.turn;
mountedGameId = store?.gameId ?? "";
mountError = null;
} catch (err) {
mountError = err instanceof Error ? err.message : String(err);
}
}
onMount(() => {
mounted = true;
onResize = (): void => {
if (handle === null || containerEl === null) return;
handle.resize(containerEl.clientWidth, containerEl.clientHeight);
};
window.addEventListener("resize", onResize);
});
onDestroy(() => {
mounted = false;
if (onResize !== null) {
window.removeEventListener("resize", onResize);
onResize = null;
}
if (handle !== null) {
handle.dispose();
handle = null;
}
});
</script> </script>
<section class="active-view" data-testid="active-view-map"> <section class="active-view" data-testid="active-view-map" data-status={store?.status ?? "idle"}>
<h2>{i18n.t("game.view.map")}</h2> {#if store?.status === "error"}
<p>{i18n.t("game.shell.coming_soon")}</p> <p class="overlay error" role="alert" data-testid="map-error">
{store.error ?? "request failed"}
</p>
{:else if mountError !== null}
<p class="overlay error" role="alert" data-testid="map-mount-error">
{mountError}
</p>
{:else if store?.status !== "ready"}
<p class="overlay" data-testid="map-loading">{i18n.t("common.loading")}</p>
{/if}
<div
class="canvas-wrap"
data-testid="map-canvas-wrap"
data-planet-count={store?.report?.planets.length ?? 0}
bind:this={containerEl}
>
<canvas bind:this={canvasEl}></canvas>
</div>
</section> </section>
<style> <style>
.active-view { .active-view {
padding: 1.5rem; position: relative;
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
}
.canvas-wrap {
flex: 1;
min-height: 0;
position: relative;
overflow: hidden;
background: #0a0e1a;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
.overlay {
position: absolute;
top: 0.75rem;
left: 50%;
transform: translateX(-50%);
padding: 0.4rem 0.9rem;
background: rgba(20, 24, 42, 0.85);
color: #e8eaf6;
border: 1px solid #2a3150;
border-radius: 6px;
z-index: 10;
font-family: system-ui, sans-serif; font-family: system-ui, sans-serif;
} font-size: 0.9rem;
.active-view h2 {
margin: 0 0 0.5rem;
font-size: 1.1rem;
}
.active-view p {
margin: 0; margin: 0;
color: #555; }
.overlay.error {
background: #4a1820;
border-color: #6d2530;
color: #ffb4b4;
} }
</style> </style>
+200
View File
@@ -0,0 +1,200 @@
// Per-game runtime state owned by the in-game shell layout
// (`routes/games/[id]/+layout.svelte`). The store discovers the
// game's current turn through `lobby.my.games.list`, fetches the
// matching `user.games.report`, and exposes a TS-friendly `GameReport`
// snapshot to every consumer (header turn counter, map view,
// inspector tabs in later phases).
//
// Phase 11 covers planets only; later phases extend the report
// surface as their slice of state lands. Every consumer reads from
// the same store instance — instantiation per game guarantees the
// layout remount on `gameId` change reseeds the snapshot, while
// navigation between active views inside the same game keeps the
// instance alive (state-preservation rule, see ui/docs/navigation.md).
import {
GameStateError,
fetchGameReport,
type GameReport,
} from "../api/game-state";
import { listMyGames, type GameSummary } from "../api/lobby";
import type { GalaxyClient } from "../api/galaxy-client";
import type { Cache } from "../platform/store/index";
import type { WrapMode } from "../map/world";
const PREF_NAMESPACE = "game-prefs";
const PREF_KEY_WRAP_MODE = (gameId: string) => `${gameId}/wrap-mode`;
/**
* GAME_STATE_CONTEXT_KEY is the Svelte context key the in-game shell
* layout uses to expose its `GameStateStore` instance to descendants.
* Header / map / inspector children resolve the store via
* `getContext(GAME_STATE_CONTEXT_KEY)`.
*/
export const GAME_STATE_CONTEXT_KEY = Symbol("game-state");
type Status = "idle" | "loading" | "ready" | "error";
export class GameStateStore {
gameId: string = $state("");
status: Status = $state("idle");
report: GameReport | null = $state(null);
wrapMode: WrapMode = $state("torus");
error: string | null = $state(null);
private client: GalaxyClient | null = null;
private cache: Cache | null = null;
private currentTurn = 0;
private destroyed = false;
private visibilityListener: (() => void) | null = null;
/**
* init kicks off the per-game lifecycle. The call is idempotent on
* the same `gameId`; calling with a different game forwards through
* `setGame` so the layout can hand off across navigations.
*/
async init(opts: {
client: GalaxyClient;
cache: Cache;
gameId: string;
}): Promise<void> {
this.client = opts.client;
this.cache = opts.cache;
await this.setGame(opts.gameId);
this.installVisibilityListener();
}
/**
* setGame switches the store to the supplied game id, fetches the
* matching lobby record to discover `current_turn`, then loads the
* report. Failure paths surface through `status === "error"` and
* the matching `error` string (already localised by the caller).
*/
async setGame(gameId: string): Promise<void> {
if (this.client === null || this.cache === null) {
throw new Error("game-state: setGame called before init");
}
this.gameId = gameId;
this.status = "loading";
this.error = null;
this.report = null;
this.wrapMode = await readWrapMode(this.cache, gameId);
try {
const summary = await this.findGame(gameId);
if (summary === null) {
this.status = "error";
this.error = `game ${gameId} is not in your list`;
return;
}
this.currentTurn = summary.currentTurn;
await this.loadTurn(summary.currentTurn);
} catch (err) {
if (this.destroyed) return;
this.status = "error";
this.error = describe(err);
}
}
/**
* setTurn loads a different turn snapshot — used by Phase 26 history
* mode. The current turn stays at whatever `setGame` discovered;
* calling without an argument refetches the same turn.
*/
async setTurn(turn: number): Promise<void> {
if (this.client === null) return;
this.status = "loading";
this.error = null;
try {
await this.loadTurn(turn);
} catch (err) {
this.status = "error";
this.error = describe(err);
}
}
/**
* refresh re-fetches the report at the current turn. Called on
* window `visibilitychange` so the map and the turn counter stay
* fresh after the user returns to the tab.
*/
refresh(): Promise<void> {
return this.setTurn(this.currentTurn);
}
/**
* setWrapMode persists the per-game preference into Cache so the
* next visit to the game restores it. Phase 29 wires the toggle UI;
* Phase 11 only reads through `wrapMode` and writes via this method.
*/
async setWrapMode(mode: WrapMode): Promise<void> {
this.wrapMode = mode;
if (this.cache !== null) {
await this.cache.put(PREF_NAMESPACE, PREF_KEY_WRAP_MODE(this.gameId), mode);
}
}
/**
* failBootstrap is used by the layout to surface errors that
* happen *before* `init` could be reached (missing keypair, missing
* gateway public key, core/store load failure). It does not need
* `init` to have run first.
*/
failBootstrap(message: string): void {
this.status = "error";
this.error = message;
}
dispose(): void {
this.destroyed = true;
if (this.visibilityListener !== null && typeof document !== "undefined") {
document.removeEventListener("visibilitychange", this.visibilityListener);
}
this.visibilityListener = null;
this.client = null;
this.cache = null;
}
private async findGame(gameId: string): Promise<GameSummary | null> {
if (this.client === null) return null;
const games = await listMyGames(this.client);
return games.find((g) => g.gameId === gameId) ?? null;
}
private async loadTurn(turn: number): Promise<void> {
if (this.client === null) return;
const report = await fetchGameReport(this.client, this.gameId, turn);
if (this.destroyed) return;
this.report = report;
this.currentTurn = turn;
this.status = "ready";
}
private installVisibilityListener(): void {
if (typeof document === "undefined") return;
const listener = (): void => {
if (document.visibilityState === "visible" && this.status === "ready") {
void this.refresh();
}
};
this.visibilityListener = listener;
document.addEventListener("visibilitychange", listener);
}
}
async function readWrapMode(cache: Cache, gameId: string): Promise<WrapMode> {
const stored = await cache.get<string>(PREF_NAMESPACE, PREF_KEY_WRAP_MODE(gameId));
if (stored === "no-wrap") return "no-wrap";
return "torus";
}
function describe(err: unknown): string {
if (err instanceof GameStateError) {
return err.message;
}
if (err instanceof Error) {
return err.message;
}
return "request failed";
}
+22 -6
View File
@@ -1,15 +1,31 @@
<!-- <!--
Phase 10 placeholder turn counter. The displayed value is the static Phase 11 turn counter: reads the live turn number from the per-game
`?` glyph from `game.shell.turn_unknown`; Phase 11 swaps the source `GameStateStore` provided through context by
to the live game state. The wrapping span is kept as the public `routes/games/[id]/+layout.svelte`. Renders the static `?` placeholder
shape so Phase 11 only needs to replace the inner text. from `game.shell.turn_unknown` when the store has not yet produced a
report (boot, network error, no membership) so the header chrome
keeps its width across loading transitions.
Phase 26 will turn this into a clickable trigger that opens the
turn navigator; Phase 24 wires push-event-driven turn-ready toasts
that may flash this counter when a new turn is ready.
--> -->
<script lang="ts"> <script lang="ts">
import { getContext } from "svelte";
import { i18n } from "$lib/i18n/index.svelte"; import { i18n } from "$lib/i18n/index.svelte";
import { GAME_STATE_CONTEXT_KEY, type GameStateStore } from "$lib/game-state.svelte";
const store = getContext<GameStateStore | undefined>(GAME_STATE_CONTEXT_KEY);
const display = $derived.by(() => {
const report = store?.report ?? null;
if (report === null) return i18n.t("game.shell.turn_unknown");
return String(report.turn);
});
</script> </script>
<span class="turn" data-testid="turn-counter"> <span class="turn" data-testid="turn-counter" data-turn={display}>
{i18n.t("game.shell.turn_label")}&nbsp;{i18n.t("game.shell.turn_unknown")} {i18n.t("game.shell.turn_label")}&nbsp;{display}
</span> </span>
<style> <style>
+98
View File
@@ -0,0 +1,98 @@
// State binding between the typed game report and the renderer's
// World. Phase 11 only emits primitives for planets; later phases
// extend the binding with ship-class reach circles (Phase 17 / 18),
// hyperspace and incoming groups (Phase 11+ via separate primitives),
// cargo routes (Phase 16), reach / visibility zones (Phase 17), and
// battle / bombing markers (Phase 27).
//
// The four planet kinds in the report each map to a distinct style so
// the user can tell own / other-race / uninhabited / unidentified
// planets apart at a glance. The exact colours are Phase 11 defaults
// chosen against the dark theme; Phase 35 polish picks final
// colours and adds theme switching.
import type { GameReport, ReportPlanet } from "../api/game-state";
import { World, type Primitive, type Style } from "./world";
const STYLE_LOCAL: Style = {
fillColor: 0x6dd2ff,
fillAlpha: 1,
pointRadiusPx: 6,
};
const STYLE_OTHER: Style = {
fillColor: 0xff8a65,
fillAlpha: 1,
pointRadiusPx: 5,
};
const STYLE_UNINHABITED: Style = {
fillColor: 0xb0bec5,
fillAlpha: 0.85,
pointRadiusPx: 4,
};
const STYLE_UNIDENTIFIED: Style = {
fillColor: 0x546e7a,
fillAlpha: 0.7,
pointRadiusPx: 3,
};
// PlanetIDs occupy the [0, 4_000_000_000) range — well below
// JavaScript's `Number.MAX_SAFE_INTEGER` — so the engine `number` (uint64)
// fits in a primitive id (number) without truncation. The binding
// uses the engine number directly as the primitive id so later phases
// can resolve a planet by its hit-test result without an extra
// lookup table.
function styleFor(kind: ReportPlanet["kind"]): Style {
switch (kind) {
case "local":
return STYLE_LOCAL;
case "other":
return STYLE_OTHER;
case "uninhabited":
return STYLE_UNINHABITED;
case "unidentified":
return STYLE_UNIDENTIFIED;
}
}
function priorityFor(kind: ReportPlanet["kind"]): number {
switch (kind) {
case "local":
return 4;
case "other":
return 3;
case "uninhabited":
return 2;
case "unidentified":
return 1;
}
}
/**
* reportToWorld translates a GameReport into a renderer-ready World
* containing one Point primitive per planet (all four planet kinds).
* The world rectangle matches `report.mapWidth` × `report.mapHeight`.
*
* If the report carries zero planets (turn-zero edge cases or seeded
* tests), the World is still well-formed: the renderer mounts on an
* empty primitive list without errors.
*/
export function reportToWorld(report: GameReport): World {
const primitives: Primitive[] = [];
for (const planet of report.planets) {
primitives.push({
kind: "point",
id: planet.number,
priority: priorityFor(planet.kind),
style: styleFor(planet.kind),
hitSlopPx: 0,
x: planet.x,
y: planet.y,
});
}
const width = report.mapWidth > 0 ? report.mapWidth : 1;
const height = report.mapHeight > 0 ? report.mapHeight : 1;
return new World(width, height, primitives);
}
@@ -0,0 +1,5 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
export { UUID, UUIDT } from './common/uuid.js';
@@ -0,0 +1,65 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class UUID implements flatbuffers.IUnpackableObject<UUIDT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):UUID {
this.bb_pos = i;
this.bb = bb;
return this;
}
hi():bigint {
return this.bb!.readUint64(this.bb_pos);
}
lo():bigint {
return this.bb!.readUint64(this.bb_pos + 8);
}
static sizeOf():number {
return 16;
}
static createUUID(builder:flatbuffers.Builder, hi: bigint, lo: bigint):flatbuffers.Offset {
builder.prep(8, 16);
builder.writeInt64(BigInt(lo ?? 0));
builder.writeInt64(BigInt(hi ?? 0));
return builder.offset();
}
unpack(): UUIDT {
return new UUIDT(
this.hi(),
this.lo()
);
}
unpackTo(_o: UUIDT): void {
_o.hi = this.hi();
_o.lo = this.lo();
}
}
export class UUIDT implements flatbuffers.IGeneratedObject {
constructor(
public hi: bigint = BigInt('0'),
public lo: bigint = BigInt('0')
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
return UUID.createUUID(builder,
this.hi,
this.lo
);
}
}
@@ -84,8 +84,13 @@ updatedAtMs():bigint {
return offset ? this.bb!.readInt64(this.bb_pos + offset) : BigInt('0'); return offset ? this.bb!.readInt64(this.bb_pos + offset) : BigInt('0');
} }
currentTurn():number {
const offset = this.bb!.__offset(this.bb_pos, 24);
return offset ? this.bb!.readInt32(this.bb_pos + offset) : 0;
}
static startGameSummary(builder:flatbuffers.Builder) { static startGameSummary(builder:flatbuffers.Builder) {
builder.startObject(10); builder.startObject(11);
} }
static addGameId(builder:flatbuffers.Builder, gameIdOffset:flatbuffers.Offset) { static addGameId(builder:flatbuffers.Builder, gameIdOffset:flatbuffers.Offset) {
@@ -128,12 +133,16 @@ static addUpdatedAtMs(builder:flatbuffers.Builder, updatedAtMs:bigint) {
builder.addFieldInt64(9, updatedAtMs, BigInt('0')); builder.addFieldInt64(9, updatedAtMs, BigInt('0'));
} }
static addCurrentTurn(builder:flatbuffers.Builder, currentTurn:number) {
builder.addFieldInt32(10, currentTurn, 0);
}
static endGameSummary(builder:flatbuffers.Builder):flatbuffers.Offset { static endGameSummary(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject(); const offset = builder.endObject();
return offset; return offset;
} }
static createGameSummary(builder:flatbuffers.Builder, gameIdOffset:flatbuffers.Offset, gameNameOffset:flatbuffers.Offset, gameTypeOffset:flatbuffers.Offset, statusOffset:flatbuffers.Offset, ownerUserIdOffset:flatbuffers.Offset, minPlayers:number, maxPlayers:number, enrollmentEndsAtMs:bigint, createdAtMs:bigint, updatedAtMs:bigint):flatbuffers.Offset { static createGameSummary(builder:flatbuffers.Builder, gameIdOffset:flatbuffers.Offset, gameNameOffset:flatbuffers.Offset, gameTypeOffset:flatbuffers.Offset, statusOffset:flatbuffers.Offset, ownerUserIdOffset:flatbuffers.Offset, minPlayers:number, maxPlayers:number, enrollmentEndsAtMs:bigint, createdAtMs:bigint, updatedAtMs:bigint, currentTurn:number):flatbuffers.Offset {
GameSummary.startGameSummary(builder); GameSummary.startGameSummary(builder);
GameSummary.addGameId(builder, gameIdOffset); GameSummary.addGameId(builder, gameIdOffset);
GameSummary.addGameName(builder, gameNameOffset); GameSummary.addGameName(builder, gameNameOffset);
@@ -145,6 +154,7 @@ static createGameSummary(builder:flatbuffers.Builder, gameIdOffset:flatbuffers.O
GameSummary.addEnrollmentEndsAtMs(builder, enrollmentEndsAtMs); GameSummary.addEnrollmentEndsAtMs(builder, enrollmentEndsAtMs);
GameSummary.addCreatedAtMs(builder, createdAtMs); GameSummary.addCreatedAtMs(builder, createdAtMs);
GameSummary.addUpdatedAtMs(builder, updatedAtMs); GameSummary.addUpdatedAtMs(builder, updatedAtMs);
GameSummary.addCurrentTurn(builder, currentTurn);
return GameSummary.endGameSummary(builder); return GameSummary.endGameSummary(builder);
} }
@@ -159,7 +169,8 @@ unpack(): GameSummaryT {
this.maxPlayers(), this.maxPlayers(),
this.enrollmentEndsAtMs(), this.enrollmentEndsAtMs(),
this.createdAtMs(), this.createdAtMs(),
this.updatedAtMs() this.updatedAtMs(),
this.currentTurn()
); );
} }
@@ -175,6 +186,7 @@ unpackTo(_o: GameSummaryT): void {
_o.enrollmentEndsAtMs = this.enrollmentEndsAtMs(); _o.enrollmentEndsAtMs = this.enrollmentEndsAtMs();
_o.createdAtMs = this.createdAtMs(); _o.createdAtMs = this.createdAtMs();
_o.updatedAtMs = this.updatedAtMs(); _o.updatedAtMs = this.updatedAtMs();
_o.currentTurn = this.currentTurn();
} }
} }
@@ -189,7 +201,8 @@ constructor(
public maxPlayers: number = 0, public maxPlayers: number = 0,
public enrollmentEndsAtMs: bigint = BigInt('0'), public enrollmentEndsAtMs: bigint = BigInt('0'),
public createdAtMs: bigint = BigInt('0'), public createdAtMs: bigint = BigInt('0'),
public updatedAtMs: bigint = BigInt('0') public updatedAtMs: bigint = BigInt('0'),
public currentTurn: number = 0
){} ){}
@@ -210,7 +223,8 @@ pack(builder:flatbuffers.Builder): flatbuffers.Offset {
this.maxPlayers, this.maxPlayers,
this.enrollmentEndsAtMs, this.enrollmentEndsAtMs,
this.createdAtMs, this.createdAtMs,
this.updatedAtMs this.updatedAtMs,
this.currentTurn
); );
} }
} }
@@ -0,0 +1,25 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
export { Bombing, BombingT } from './report/bombing.js';
export { GameReportRequest, GameReportRequestT } from './report/game-report-request.js';
export { IncomingGroup, IncomingGroupT } from './report/incoming-group.js';
export { LocalFleet, LocalFleetT } from './report/local-fleet.js';
export { LocalGroup, LocalGroupT } from './report/local-group.js';
export { LocalPlanet, LocalPlanetT } from './report/local-planet.js';
export { OtherGroup, OtherGroupT } from './report/other-group.js';
export { OtherPlanet, OtherPlanetT } from './report/other-planet.js';
export { OtherScience, OtherScienceT } from './report/other-science.js';
export { OthersShipClass, OthersShipClassT } from './report/others-ship-class.js';
export { Player, PlayerT } from './report/player.js';
export { Report, ReportT } from './report/report.js';
export { Route, RouteT } from './report/route.js';
export { RouteEntry, RouteEntryT } from './report/route-entry.js';
export { Science, ScienceT } from './report/science.js';
export { ShipClass, ShipClassT } from './report/ship-class.js';
export { ShipProduction, ShipProductionT } from './report/ship-production.js';
export { TechEntry, TechEntryT } from './report/tech-entry.js';
export { UnidentifiedGroup, UnidentifiedGroupT } from './report/unidentified-group.js';
export { UnidentifiedPlanet, UnidentifiedPlanetT } from './report/unidentified-planet.js';
export { UninhabitedPlanet, UninhabitedPlanetT } from './report/uninhabited-planet.js';
@@ -0,0 +1,241 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class Bombing implements flatbuffers.IUnpackableObject<BombingT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):Bombing {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsBombing(bb:flatbuffers.ByteBuffer, obj?:Bombing):Bombing {
return (obj || new Bombing()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsBombing(bb:flatbuffers.ByteBuffer, obj?:Bombing):Bombing {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new Bombing()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
number():bigint {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
planet():string|null
planet(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
planet(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
owner():string|null
owner(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
owner(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
attacker():string|null
attacker(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
attacker(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
production():string|null
production(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
production(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
industry():number {
const offset = this.bb!.__offset(this.bb_pos, 14);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
population():number {
const offset = this.bb!.__offset(this.bb_pos, 16);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
colonists():number {
const offset = this.bb!.__offset(this.bb_pos, 18);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
capital():number {
const offset = this.bb!.__offset(this.bb_pos, 20);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
material():number {
const offset = this.bb!.__offset(this.bb_pos, 22);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
attackPower():number {
const offset = this.bb!.__offset(this.bb_pos, 24);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
wiped():boolean {
const offset = this.bb!.__offset(this.bb_pos, 26);
return offset ? !!this.bb!.readInt8(this.bb_pos + offset) : false;
}
static startBombing(builder:flatbuffers.Builder) {
builder.startObject(12);
}
static addNumber(builder:flatbuffers.Builder, number:bigint) {
builder.addFieldInt64(0, number, BigInt('0'));
}
static addPlanet(builder:flatbuffers.Builder, planetOffset:flatbuffers.Offset) {
builder.addFieldOffset(1, planetOffset, 0);
}
static addOwner(builder:flatbuffers.Builder, ownerOffset:flatbuffers.Offset) {
builder.addFieldOffset(2, ownerOffset, 0);
}
static addAttacker(builder:flatbuffers.Builder, attackerOffset:flatbuffers.Offset) {
builder.addFieldOffset(3, attackerOffset, 0);
}
static addProduction(builder:flatbuffers.Builder, productionOffset:flatbuffers.Offset) {
builder.addFieldOffset(4, productionOffset, 0);
}
static addIndustry(builder:flatbuffers.Builder, industry:number) {
builder.addFieldFloat32(5, industry, 0.0);
}
static addPopulation(builder:flatbuffers.Builder, population:number) {
builder.addFieldFloat32(6, population, 0.0);
}
static addColonists(builder:flatbuffers.Builder, colonists:number) {
builder.addFieldFloat32(7, colonists, 0.0);
}
static addCapital(builder:flatbuffers.Builder, capital:number) {
builder.addFieldFloat32(8, capital, 0.0);
}
static addMaterial(builder:flatbuffers.Builder, material:number) {
builder.addFieldFloat32(9, material, 0.0);
}
static addAttackPower(builder:flatbuffers.Builder, attackPower:number) {
builder.addFieldFloat32(10, attackPower, 0.0);
}
static addWiped(builder:flatbuffers.Builder, wiped:boolean) {
builder.addFieldInt8(11, +wiped, +false);
}
static endBombing(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createBombing(builder:flatbuffers.Builder, number:bigint, planetOffset:flatbuffers.Offset, ownerOffset:flatbuffers.Offset, attackerOffset:flatbuffers.Offset, productionOffset:flatbuffers.Offset, industry:number, population:number, colonists:number, capital:number, material:number, attackPower:number, wiped:boolean):flatbuffers.Offset {
Bombing.startBombing(builder);
Bombing.addNumber(builder, number);
Bombing.addPlanet(builder, planetOffset);
Bombing.addOwner(builder, ownerOffset);
Bombing.addAttacker(builder, attackerOffset);
Bombing.addProduction(builder, productionOffset);
Bombing.addIndustry(builder, industry);
Bombing.addPopulation(builder, population);
Bombing.addColonists(builder, colonists);
Bombing.addCapital(builder, capital);
Bombing.addMaterial(builder, material);
Bombing.addAttackPower(builder, attackPower);
Bombing.addWiped(builder, wiped);
return Bombing.endBombing(builder);
}
unpack(): BombingT {
return new BombingT(
this.number(),
this.planet(),
this.owner(),
this.attacker(),
this.production(),
this.industry(),
this.population(),
this.colonists(),
this.capital(),
this.material(),
this.attackPower(),
this.wiped()
);
}
unpackTo(_o: BombingT): void {
_o.number = this.number();
_o.planet = this.planet();
_o.owner = this.owner();
_o.attacker = this.attacker();
_o.production = this.production();
_o.industry = this.industry();
_o.population = this.population();
_o.colonists = this.colonists();
_o.capital = this.capital();
_o.material = this.material();
_o.attackPower = this.attackPower();
_o.wiped = this.wiped();
}
}
export class BombingT implements flatbuffers.IGeneratedObject {
constructor(
public number: bigint = BigInt('0'),
public planet: string|Uint8Array|null = null,
public owner: string|Uint8Array|null = null,
public attacker: string|Uint8Array|null = null,
public production: string|Uint8Array|null = null,
public industry: number = 0.0,
public population: number = 0.0,
public colonists: number = 0.0,
public capital: number = 0.0,
public material: number = 0.0,
public attackPower: number = 0.0,
public wiped: boolean = false
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const planet = (this.planet !== null ? builder.createString(this.planet!) : 0);
const owner = (this.owner !== null ? builder.createString(this.owner!) : 0);
const attacker = (this.attacker !== null ? builder.createString(this.attacker!) : 0);
const production = (this.production !== null ? builder.createString(this.production!) : 0);
return Bombing.createBombing(builder,
this.number,
planet,
owner,
attacker,
production,
this.industry,
this.population,
this.colonists,
this.capital,
this.material,
this.attackPower,
this.wiped
);
}
}
@@ -0,0 +1,90 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
import { UUID, UUIDT } from '../common/uuid.js';
export class GameReportRequest implements flatbuffers.IUnpackableObject<GameReportRequestT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):GameReportRequest {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsGameReportRequest(bb:flatbuffers.ByteBuffer, obj?:GameReportRequest):GameReportRequest {
return (obj || new GameReportRequest()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsGameReportRequest(bb:flatbuffers.ByteBuffer, obj?:GameReportRequest):GameReportRequest {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new GameReportRequest()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
gameId(obj?:UUID):UUID|null {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? (obj || new UUID()).__init(this.bb_pos + offset, this.bb!) : null;
}
turn():number {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.readUint32(this.bb_pos + offset) : 0;
}
static startGameReportRequest(builder:flatbuffers.Builder) {
builder.startObject(2);
}
static addGameId(builder:flatbuffers.Builder, gameIdOffset:flatbuffers.Offset) {
builder.addFieldStruct(0, gameIdOffset, 0);
}
static addTurn(builder:flatbuffers.Builder, turn:number) {
builder.addFieldInt32(1, turn, 0);
}
static endGameReportRequest(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
builder.requiredField(offset, 4) // game_id
return offset;
}
static createGameReportRequest(builder:flatbuffers.Builder, gameIdOffset:flatbuffers.Offset, turn:number):flatbuffers.Offset {
GameReportRequest.startGameReportRequest(builder);
GameReportRequest.addGameId(builder, gameIdOffset);
GameReportRequest.addTurn(builder, turn);
return GameReportRequest.endGameReportRequest(builder);
}
unpack(): GameReportRequestT {
return new GameReportRequestT(
(this.gameId() !== null ? this.gameId()!.unpack() : null),
this.turn()
);
}
unpackTo(_o: GameReportRequestT): void {
_o.gameId = (this.gameId() !== null ? this.gameId()!.unpack() : null);
_o.turn = this.turn();
}
}
export class GameReportRequestT implements flatbuffers.IGeneratedObject {
constructor(
public gameId: UUIDT|null = null,
public turn: number = 0
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
return GameReportRequest.createGameReportRequest(builder,
(this.gameId !== null ? this.gameId!.pack(builder) : 0),
this.turn
);
}
}
@@ -0,0 +1,130 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class IncomingGroup implements flatbuffers.IUnpackableObject<IncomingGroupT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):IncomingGroup {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsIncomingGroup(bb:flatbuffers.ByteBuffer, obj?:IncomingGroup):IncomingGroup {
return (obj || new IncomingGroup()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsIncomingGroup(bb:flatbuffers.ByteBuffer, obj?:IncomingGroup):IncomingGroup {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new IncomingGroup()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
origin():bigint {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
destination():bigint {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
distance():number {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
speed():number {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
mass():number {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
static startIncomingGroup(builder:flatbuffers.Builder) {
builder.startObject(5);
}
static addOrigin(builder:flatbuffers.Builder, origin:bigint) {
builder.addFieldInt64(0, origin, BigInt('0'));
}
static addDestination(builder:flatbuffers.Builder, destination:bigint) {
builder.addFieldInt64(1, destination, BigInt('0'));
}
static addDistance(builder:flatbuffers.Builder, distance:number) {
builder.addFieldFloat32(2, distance, 0.0);
}
static addSpeed(builder:flatbuffers.Builder, speed:number) {
builder.addFieldFloat32(3, speed, 0.0);
}
static addMass(builder:flatbuffers.Builder, mass:number) {
builder.addFieldFloat32(4, mass, 0.0);
}
static endIncomingGroup(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createIncomingGroup(builder:flatbuffers.Builder, origin:bigint, destination:bigint, distance:number, speed:number, mass:number):flatbuffers.Offset {
IncomingGroup.startIncomingGroup(builder);
IncomingGroup.addOrigin(builder, origin);
IncomingGroup.addDestination(builder, destination);
IncomingGroup.addDistance(builder, distance);
IncomingGroup.addSpeed(builder, speed);
IncomingGroup.addMass(builder, mass);
return IncomingGroup.endIncomingGroup(builder);
}
unpack(): IncomingGroupT {
return new IncomingGroupT(
this.origin(),
this.destination(),
this.distance(),
this.speed(),
this.mass()
);
}
unpackTo(_o: IncomingGroupT): void {
_o.origin = this.origin();
_o.destination = this.destination();
_o.distance = this.distance();
_o.speed = this.speed();
_o.mass = this.mass();
}
}
export class IncomingGroupT implements flatbuffers.IGeneratedObject {
constructor(
public origin: bigint = BigInt('0'),
public destination: bigint = BigInt('0'),
public distance: number = 0.0,
public speed: number = 0.0,
public mass: number = 0.0
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
return IncomingGroup.createIncomingGroup(builder,
this.origin,
this.destination,
this.distance,
this.speed,
this.mass
);
}
}
@@ -0,0 +1,167 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class LocalFleet implements flatbuffers.IUnpackableObject<LocalFleetT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):LocalFleet {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsLocalFleet(bb:flatbuffers.ByteBuffer, obj?:LocalFleet):LocalFleet {
return (obj || new LocalFleet()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsLocalFleet(bb:flatbuffers.ByteBuffer, obj?:LocalFleet):LocalFleet {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new LocalFleet()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
name():string|null
name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
name(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
groups():bigint {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
destination():bigint {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
origin():bigint|null {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : null;
}
range():number|null {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : null;
}
speed():number {
const offset = this.bb!.__offset(this.bb_pos, 14);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
state():string|null
state(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
state(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 16);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
static startLocalFleet(builder:flatbuffers.Builder) {
builder.startObject(7);
}
static addName(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset) {
builder.addFieldOffset(0, nameOffset, 0);
}
static addGroups(builder:flatbuffers.Builder, groups:bigint) {
builder.addFieldInt64(1, groups, BigInt('0'));
}
static addDestination(builder:flatbuffers.Builder, destination:bigint) {
builder.addFieldInt64(2, destination, BigInt('0'));
}
static addOrigin(builder:flatbuffers.Builder, origin:bigint) {
builder.addFieldInt64(3, origin, null);
}
static addRange(builder:flatbuffers.Builder, range:number) {
builder.addFieldFloat32(4, range, null);
}
static addSpeed(builder:flatbuffers.Builder, speed:number) {
builder.addFieldFloat32(5, speed, 0.0);
}
static addState(builder:flatbuffers.Builder, stateOffset:flatbuffers.Offset) {
builder.addFieldOffset(6, stateOffset, 0);
}
static endLocalFleet(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createLocalFleet(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset, groups:bigint, destination:bigint, origin:bigint|null, range:number|null, speed:number, stateOffset:flatbuffers.Offset):flatbuffers.Offset {
LocalFleet.startLocalFleet(builder);
LocalFleet.addName(builder, nameOffset);
LocalFleet.addGroups(builder, groups);
LocalFleet.addDestination(builder, destination);
if (origin !== null)
LocalFleet.addOrigin(builder, origin);
if (range !== null)
LocalFleet.addRange(builder, range);
LocalFleet.addSpeed(builder, speed);
LocalFleet.addState(builder, stateOffset);
return LocalFleet.endLocalFleet(builder);
}
unpack(): LocalFleetT {
return new LocalFleetT(
this.name(),
this.groups(),
this.destination(),
this.origin(),
this.range(),
this.speed(),
this.state()
);
}
unpackTo(_o: LocalFleetT): void {
_o.name = this.name();
_o.groups = this.groups();
_o.destination = this.destination();
_o.origin = this.origin();
_o.range = this.range();
_o.speed = this.speed();
_o.state = this.state();
}
}
export class LocalFleetT implements flatbuffers.IGeneratedObject {
constructor(
public name: string|Uint8Array|null = null,
public groups: bigint = BigInt('0'),
public destination: bigint = BigInt('0'),
public origin: bigint|null = null,
public range: number|null = null,
public speed: number = 0.0,
public state: string|Uint8Array|null = null
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const name = (this.name !== null ? builder.createString(this.name!) : 0);
const state = (this.state !== null ? builder.createString(this.state!) : 0);
return LocalFleet.createLocalFleet(builder,
name,
this.groups,
this.destination,
this.origin,
this.range,
this.speed,
state
);
}
}
@@ -0,0 +1,262 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
import { UUID, UUIDT } from '../common/uuid.js';
import { TechEntry, TechEntryT } from './tech-entry.js';
export class LocalGroup implements flatbuffers.IUnpackableObject<LocalGroupT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):LocalGroup {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsLocalGroup(bb:flatbuffers.ByteBuffer, obj?:LocalGroup):LocalGroup {
return (obj || new LocalGroup()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsLocalGroup(bb:flatbuffers.ByteBuffer, obj?:LocalGroup):LocalGroup {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new LocalGroup()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
number():bigint {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
class_():string|null
class_(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
class_(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
tech(index: number, obj?:TechEntry):TechEntry|null {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? (obj || new TechEntry()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
techLength():number {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
cargo():string|null
cargo(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
cargo(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
load():number {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
destination():bigint {
const offset = this.bb!.__offset(this.bb_pos, 14);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
origin():bigint|null {
const offset = this.bb!.__offset(this.bb_pos, 16);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : null;
}
range():number|null {
const offset = this.bb!.__offset(this.bb_pos, 18);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : null;
}
speed():number {
const offset = this.bb!.__offset(this.bb_pos, 20);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
mass():number {
const offset = this.bb!.__offset(this.bb_pos, 22);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
id(obj?:UUID):UUID|null {
const offset = this.bb!.__offset(this.bb_pos, 24);
return offset ? (obj || new UUID()).__init(this.bb_pos + offset, this.bb!) : null;
}
state():string|null
state(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
state(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 26);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
fleet():string|null
fleet(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
fleet(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 28);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
static startLocalGroup(builder:flatbuffers.Builder) {
builder.startObject(13);
}
static addNumber(builder:flatbuffers.Builder, number:bigint) {
builder.addFieldInt64(0, number, BigInt('0'));
}
static addClass(builder:flatbuffers.Builder, class_Offset:flatbuffers.Offset) {
builder.addFieldOffset(1, class_Offset, 0);
}
static addTech(builder:flatbuffers.Builder, techOffset:flatbuffers.Offset) {
builder.addFieldOffset(2, techOffset, 0);
}
static createTechVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startTechVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addCargo(builder:flatbuffers.Builder, cargoOffset:flatbuffers.Offset) {
builder.addFieldOffset(3, cargoOffset, 0);
}
static addLoad(builder:flatbuffers.Builder, load:number) {
builder.addFieldFloat32(4, load, 0.0);
}
static addDestination(builder:flatbuffers.Builder, destination:bigint) {
builder.addFieldInt64(5, destination, BigInt('0'));
}
static addOrigin(builder:flatbuffers.Builder, origin:bigint) {
builder.addFieldInt64(6, origin, null);
}
static addRange(builder:flatbuffers.Builder, range:number) {
builder.addFieldFloat32(7, range, null);
}
static addSpeed(builder:flatbuffers.Builder, speed:number) {
builder.addFieldFloat32(8, speed, 0.0);
}
static addMass(builder:flatbuffers.Builder, mass:number) {
builder.addFieldFloat32(9, mass, 0.0);
}
static addId(builder:flatbuffers.Builder, idOffset:flatbuffers.Offset) {
builder.addFieldStruct(10, idOffset, 0);
}
static addState(builder:flatbuffers.Builder, stateOffset:flatbuffers.Offset) {
builder.addFieldOffset(11, stateOffset, 0);
}
static addFleet(builder:flatbuffers.Builder, fleetOffset:flatbuffers.Offset) {
builder.addFieldOffset(12, fleetOffset, 0);
}
static endLocalGroup(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
builder.requiredField(offset, 24) // id
return offset;
}
unpack(): LocalGroupT {
return new LocalGroupT(
this.number(),
this.class_(),
this.bb!.createObjList<TechEntry, TechEntryT>(this.tech.bind(this), this.techLength()),
this.cargo(),
this.load(),
this.destination(),
this.origin(),
this.range(),
this.speed(),
this.mass(),
(this.id() !== null ? this.id()!.unpack() : null),
this.state(),
this.fleet()
);
}
unpackTo(_o: LocalGroupT): void {
_o.number = this.number();
_o.class_ = this.class_();
_o.tech = this.bb!.createObjList<TechEntry, TechEntryT>(this.tech.bind(this), this.techLength());
_o.cargo = this.cargo();
_o.load = this.load();
_o.destination = this.destination();
_o.origin = this.origin();
_o.range = this.range();
_o.speed = this.speed();
_o.mass = this.mass();
_o.id = (this.id() !== null ? this.id()!.unpack() : null);
_o.state = this.state();
_o.fleet = this.fleet();
}
}
export class LocalGroupT implements flatbuffers.IGeneratedObject {
constructor(
public number: bigint = BigInt('0'),
public class_: string|Uint8Array|null = null,
public tech: (TechEntryT)[] = [],
public cargo: string|Uint8Array|null = null,
public load: number = 0.0,
public destination: bigint = BigInt('0'),
public origin: bigint|null = null,
public range: number|null = null,
public speed: number = 0.0,
public mass: number = 0.0,
public id: UUIDT|null = null,
public state: string|Uint8Array|null = null,
public fleet: string|Uint8Array|null = null
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const class_ = (this.class_ !== null ? builder.createString(this.class_!) : 0);
const tech = LocalGroup.createTechVector(builder, builder.createObjectOffsetList(this.tech));
const cargo = (this.cargo !== null ? builder.createString(this.cargo!) : 0);
const state = (this.state !== null ? builder.createString(this.state!) : 0);
const fleet = (this.fleet !== null ? builder.createString(this.fleet!) : 0);
LocalGroup.startLocalGroup(builder);
LocalGroup.addNumber(builder, this.number);
LocalGroup.addClass(builder, class_);
LocalGroup.addTech(builder, tech);
LocalGroup.addCargo(builder, cargo);
LocalGroup.addLoad(builder, this.load);
LocalGroup.addDestination(builder, this.destination);
if (this.origin !== null)
LocalGroup.addOrigin(builder, this.origin);
if (this.range !== null)
LocalGroup.addRange(builder, this.range);
LocalGroup.addSpeed(builder, this.speed);
LocalGroup.addMass(builder, this.mass);
LocalGroup.addId(builder, (this.id !== null ? this.id!.pack(builder) : 0));
LocalGroup.addState(builder, state);
LocalGroup.addFleet(builder, fleet);
return LocalGroup.endLocalGroup(builder);
}
}
@@ -0,0 +1,249 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class LocalPlanet implements flatbuffers.IUnpackableObject<LocalPlanetT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):LocalPlanet {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsLocalPlanet(bb:flatbuffers.ByteBuffer, obj?:LocalPlanet):LocalPlanet {
return (obj || new LocalPlanet()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsLocalPlanet(bb:flatbuffers.ByteBuffer, obj?:LocalPlanet):LocalPlanet {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new LocalPlanet()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
x():number {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
y():number {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
number():bigint {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
size():number {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
name():string|null
name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
name(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
resources():number {
const offset = this.bb!.__offset(this.bb_pos, 14);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
capital():number {
const offset = this.bb!.__offset(this.bb_pos, 16);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
material():number {
const offset = this.bb!.__offset(this.bb_pos, 18);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
industry():number {
const offset = this.bb!.__offset(this.bb_pos, 20);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
population():number {
const offset = this.bb!.__offset(this.bb_pos, 22);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
colonists():number {
const offset = this.bb!.__offset(this.bb_pos, 24);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
production():string|null
production(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
production(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 26);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
freeIndustry():number {
const offset = this.bb!.__offset(this.bb_pos, 28);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
static startLocalPlanet(builder:flatbuffers.Builder) {
builder.startObject(13);
}
static addX(builder:flatbuffers.Builder, x:number) {
builder.addFieldFloat32(0, x, 0.0);
}
static addY(builder:flatbuffers.Builder, y:number) {
builder.addFieldFloat32(1, y, 0.0);
}
static addNumber(builder:flatbuffers.Builder, number:bigint) {
builder.addFieldInt64(2, number, BigInt('0'));
}
static addSize(builder:flatbuffers.Builder, size:number) {
builder.addFieldFloat32(3, size, 0.0);
}
static addName(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset) {
builder.addFieldOffset(4, nameOffset, 0);
}
static addResources(builder:flatbuffers.Builder, resources:number) {
builder.addFieldFloat32(5, resources, 0.0);
}
static addCapital(builder:flatbuffers.Builder, capital:number) {
builder.addFieldFloat32(6, capital, 0.0);
}
static addMaterial(builder:flatbuffers.Builder, material:number) {
builder.addFieldFloat32(7, material, 0.0);
}
static addIndustry(builder:flatbuffers.Builder, industry:number) {
builder.addFieldFloat32(8, industry, 0.0);
}
static addPopulation(builder:flatbuffers.Builder, population:number) {
builder.addFieldFloat32(9, population, 0.0);
}
static addColonists(builder:flatbuffers.Builder, colonists:number) {
builder.addFieldFloat32(10, colonists, 0.0);
}
static addProduction(builder:flatbuffers.Builder, productionOffset:flatbuffers.Offset) {
builder.addFieldOffset(11, productionOffset, 0);
}
static addFreeIndustry(builder:flatbuffers.Builder, freeIndustry:number) {
builder.addFieldFloat32(12, freeIndustry, 0.0);
}
static endLocalPlanet(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createLocalPlanet(builder:flatbuffers.Builder, x:number, y:number, number:bigint, size:number, nameOffset:flatbuffers.Offset, resources:number, capital:number, material:number, industry:number, population:number, colonists:number, productionOffset:flatbuffers.Offset, freeIndustry:number):flatbuffers.Offset {
LocalPlanet.startLocalPlanet(builder);
LocalPlanet.addX(builder, x);
LocalPlanet.addY(builder, y);
LocalPlanet.addNumber(builder, number);
LocalPlanet.addSize(builder, size);
LocalPlanet.addName(builder, nameOffset);
LocalPlanet.addResources(builder, resources);
LocalPlanet.addCapital(builder, capital);
LocalPlanet.addMaterial(builder, material);
LocalPlanet.addIndustry(builder, industry);
LocalPlanet.addPopulation(builder, population);
LocalPlanet.addColonists(builder, colonists);
LocalPlanet.addProduction(builder, productionOffset);
LocalPlanet.addFreeIndustry(builder, freeIndustry);
return LocalPlanet.endLocalPlanet(builder);
}
unpack(): LocalPlanetT {
return new LocalPlanetT(
this.x(),
this.y(),
this.number(),
this.size(),
this.name(),
this.resources(),
this.capital(),
this.material(),
this.industry(),
this.population(),
this.colonists(),
this.production(),
this.freeIndustry()
);
}
unpackTo(_o: LocalPlanetT): void {
_o.x = this.x();
_o.y = this.y();
_o.number = this.number();
_o.size = this.size();
_o.name = this.name();
_o.resources = this.resources();
_o.capital = this.capital();
_o.material = this.material();
_o.industry = this.industry();
_o.population = this.population();
_o.colonists = this.colonists();
_o.production = this.production();
_o.freeIndustry = this.freeIndustry();
}
}
export class LocalPlanetT implements flatbuffers.IGeneratedObject {
constructor(
public x: number = 0.0,
public y: number = 0.0,
public number: bigint = BigInt('0'),
public size: number = 0.0,
public name: string|Uint8Array|null = null,
public resources: number = 0.0,
public capital: number = 0.0,
public material: number = 0.0,
public industry: number = 0.0,
public population: number = 0.0,
public colonists: number = 0.0,
public production: string|Uint8Array|null = null,
public freeIndustry: number = 0.0
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const name = (this.name !== null ? builder.createString(this.name!) : 0);
const production = (this.production !== null ? builder.createString(this.production!) : 0);
return LocalPlanet.createLocalPlanet(builder,
this.x,
this.y,
this.number,
this.size,
name,
this.resources,
this.capital,
this.material,
this.industry,
this.population,
this.colonists,
production,
this.freeIndustry
);
}
}
@@ -0,0 +1,228 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
import { TechEntry, TechEntryT } from './tech-entry.js';
export class OtherGroup implements flatbuffers.IUnpackableObject<OtherGroupT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):OtherGroup {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsOtherGroup(bb:flatbuffers.ByteBuffer, obj?:OtherGroup):OtherGroup {
return (obj || new OtherGroup()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsOtherGroup(bb:flatbuffers.ByteBuffer, obj?:OtherGroup):OtherGroup {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new OtherGroup()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
number():bigint {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
class_():string|null
class_(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
class_(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
tech(index: number, obj?:TechEntry):TechEntry|null {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? (obj || new TechEntry()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
techLength():number {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
cargo():string|null
cargo(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
cargo(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
load():number {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
destination():bigint {
const offset = this.bb!.__offset(this.bb_pos, 14);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
origin():bigint|null {
const offset = this.bb!.__offset(this.bb_pos, 16);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : null;
}
range():number|null {
const offset = this.bb!.__offset(this.bb_pos, 18);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : null;
}
speed():number {
const offset = this.bb!.__offset(this.bb_pos, 20);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
mass():number {
const offset = this.bb!.__offset(this.bb_pos, 22);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
static startOtherGroup(builder:flatbuffers.Builder) {
builder.startObject(10);
}
static addNumber(builder:flatbuffers.Builder, number:bigint) {
builder.addFieldInt64(0, number, BigInt('0'));
}
static addClass(builder:flatbuffers.Builder, class_Offset:flatbuffers.Offset) {
builder.addFieldOffset(1, class_Offset, 0);
}
static addTech(builder:flatbuffers.Builder, techOffset:flatbuffers.Offset) {
builder.addFieldOffset(2, techOffset, 0);
}
static createTechVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startTechVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addCargo(builder:flatbuffers.Builder, cargoOffset:flatbuffers.Offset) {
builder.addFieldOffset(3, cargoOffset, 0);
}
static addLoad(builder:flatbuffers.Builder, load:number) {
builder.addFieldFloat32(4, load, 0.0);
}
static addDestination(builder:flatbuffers.Builder, destination:bigint) {
builder.addFieldInt64(5, destination, BigInt('0'));
}
static addOrigin(builder:flatbuffers.Builder, origin:bigint) {
builder.addFieldInt64(6, origin, null);
}
static addRange(builder:flatbuffers.Builder, range:number) {
builder.addFieldFloat32(7, range, null);
}
static addSpeed(builder:flatbuffers.Builder, speed:number) {
builder.addFieldFloat32(8, speed, 0.0);
}
static addMass(builder:flatbuffers.Builder, mass:number) {
builder.addFieldFloat32(9, mass, 0.0);
}
static endOtherGroup(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createOtherGroup(builder:flatbuffers.Builder, number:bigint, class_Offset:flatbuffers.Offset, techOffset:flatbuffers.Offset, cargoOffset:flatbuffers.Offset, load:number, destination:bigint, origin:bigint|null, range:number|null, speed:number, mass:number):flatbuffers.Offset {
OtherGroup.startOtherGroup(builder);
OtherGroup.addNumber(builder, number);
OtherGroup.addClass(builder, class_Offset);
OtherGroup.addTech(builder, techOffset);
OtherGroup.addCargo(builder, cargoOffset);
OtherGroup.addLoad(builder, load);
OtherGroup.addDestination(builder, destination);
if (origin !== null)
OtherGroup.addOrigin(builder, origin);
if (range !== null)
OtherGroup.addRange(builder, range);
OtherGroup.addSpeed(builder, speed);
OtherGroup.addMass(builder, mass);
return OtherGroup.endOtherGroup(builder);
}
unpack(): OtherGroupT {
return new OtherGroupT(
this.number(),
this.class_(),
this.bb!.createObjList<TechEntry, TechEntryT>(this.tech.bind(this), this.techLength()),
this.cargo(),
this.load(),
this.destination(),
this.origin(),
this.range(),
this.speed(),
this.mass()
);
}
unpackTo(_o: OtherGroupT): void {
_o.number = this.number();
_o.class_ = this.class_();
_o.tech = this.bb!.createObjList<TechEntry, TechEntryT>(this.tech.bind(this), this.techLength());
_o.cargo = this.cargo();
_o.load = this.load();
_o.destination = this.destination();
_o.origin = this.origin();
_o.range = this.range();
_o.speed = this.speed();
_o.mass = this.mass();
}
}
export class OtherGroupT implements flatbuffers.IGeneratedObject {
constructor(
public number: bigint = BigInt('0'),
public class_: string|Uint8Array|null = null,
public tech: (TechEntryT)[] = [],
public cargo: string|Uint8Array|null = null,
public load: number = 0.0,
public destination: bigint = BigInt('0'),
public origin: bigint|null = null,
public range: number|null = null,
public speed: number = 0.0,
public mass: number = 0.0
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const class_ = (this.class_ !== null ? builder.createString(this.class_!) : 0);
const tech = OtherGroup.createTechVector(builder, builder.createObjectOffsetList(this.tech));
const cargo = (this.cargo !== null ? builder.createString(this.cargo!) : 0);
return OtherGroup.createOtherGroup(builder,
this.number,
class_,
tech,
cargo,
this.load,
this.destination,
this.origin,
this.range,
this.speed,
this.mass
);
}
}
@@ -0,0 +1,266 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class OtherPlanet implements flatbuffers.IUnpackableObject<OtherPlanetT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):OtherPlanet {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsOtherPlanet(bb:flatbuffers.ByteBuffer, obj?:OtherPlanet):OtherPlanet {
return (obj || new OtherPlanet()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsOtherPlanet(bb:flatbuffers.ByteBuffer, obj?:OtherPlanet):OtherPlanet {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new OtherPlanet()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
owner():string|null
owner(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
owner(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
x():number {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
y():number {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
number():bigint {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
size():number {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
name():string|null
name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
name(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 14);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
resources():number {
const offset = this.bb!.__offset(this.bb_pos, 16);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
capital():number {
const offset = this.bb!.__offset(this.bb_pos, 18);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
material():number {
const offset = this.bb!.__offset(this.bb_pos, 20);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
industry():number {
const offset = this.bb!.__offset(this.bb_pos, 22);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
population():number {
const offset = this.bb!.__offset(this.bb_pos, 24);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
colonists():number {
const offset = this.bb!.__offset(this.bb_pos, 26);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
production():string|null
production(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
production(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 28);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
freeIndustry():number {
const offset = this.bb!.__offset(this.bb_pos, 30);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
static startOtherPlanet(builder:flatbuffers.Builder) {
builder.startObject(14);
}
static addOwner(builder:flatbuffers.Builder, ownerOffset:flatbuffers.Offset) {
builder.addFieldOffset(0, ownerOffset, 0);
}
static addX(builder:flatbuffers.Builder, x:number) {
builder.addFieldFloat32(1, x, 0.0);
}
static addY(builder:flatbuffers.Builder, y:number) {
builder.addFieldFloat32(2, y, 0.0);
}
static addNumber(builder:flatbuffers.Builder, number:bigint) {
builder.addFieldInt64(3, number, BigInt('0'));
}
static addSize(builder:flatbuffers.Builder, size:number) {
builder.addFieldFloat32(4, size, 0.0);
}
static addName(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset) {
builder.addFieldOffset(5, nameOffset, 0);
}
static addResources(builder:flatbuffers.Builder, resources:number) {
builder.addFieldFloat32(6, resources, 0.0);
}
static addCapital(builder:flatbuffers.Builder, capital:number) {
builder.addFieldFloat32(7, capital, 0.0);
}
static addMaterial(builder:flatbuffers.Builder, material:number) {
builder.addFieldFloat32(8, material, 0.0);
}
static addIndustry(builder:flatbuffers.Builder, industry:number) {
builder.addFieldFloat32(9, industry, 0.0);
}
static addPopulation(builder:flatbuffers.Builder, population:number) {
builder.addFieldFloat32(10, population, 0.0);
}
static addColonists(builder:flatbuffers.Builder, colonists:number) {
builder.addFieldFloat32(11, colonists, 0.0);
}
static addProduction(builder:flatbuffers.Builder, productionOffset:flatbuffers.Offset) {
builder.addFieldOffset(12, productionOffset, 0);
}
static addFreeIndustry(builder:flatbuffers.Builder, freeIndustry:number) {
builder.addFieldFloat32(13, freeIndustry, 0.0);
}
static endOtherPlanet(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createOtherPlanet(builder:flatbuffers.Builder, ownerOffset:flatbuffers.Offset, x:number, y:number, number:bigint, size:number, nameOffset:flatbuffers.Offset, resources:number, capital:number, material:number, industry:number, population:number, colonists:number, productionOffset:flatbuffers.Offset, freeIndustry:number):flatbuffers.Offset {
OtherPlanet.startOtherPlanet(builder);
OtherPlanet.addOwner(builder, ownerOffset);
OtherPlanet.addX(builder, x);
OtherPlanet.addY(builder, y);
OtherPlanet.addNumber(builder, number);
OtherPlanet.addSize(builder, size);
OtherPlanet.addName(builder, nameOffset);
OtherPlanet.addResources(builder, resources);
OtherPlanet.addCapital(builder, capital);
OtherPlanet.addMaterial(builder, material);
OtherPlanet.addIndustry(builder, industry);
OtherPlanet.addPopulation(builder, population);
OtherPlanet.addColonists(builder, colonists);
OtherPlanet.addProduction(builder, productionOffset);
OtherPlanet.addFreeIndustry(builder, freeIndustry);
return OtherPlanet.endOtherPlanet(builder);
}
unpack(): OtherPlanetT {
return new OtherPlanetT(
this.owner(),
this.x(),
this.y(),
this.number(),
this.size(),
this.name(),
this.resources(),
this.capital(),
this.material(),
this.industry(),
this.population(),
this.colonists(),
this.production(),
this.freeIndustry()
);
}
unpackTo(_o: OtherPlanetT): void {
_o.owner = this.owner();
_o.x = this.x();
_o.y = this.y();
_o.number = this.number();
_o.size = this.size();
_o.name = this.name();
_o.resources = this.resources();
_o.capital = this.capital();
_o.material = this.material();
_o.industry = this.industry();
_o.population = this.population();
_o.colonists = this.colonists();
_o.production = this.production();
_o.freeIndustry = this.freeIndustry();
}
}
export class OtherPlanetT implements flatbuffers.IGeneratedObject {
constructor(
public owner: string|Uint8Array|null = null,
public x: number = 0.0,
public y: number = 0.0,
public number: bigint = BigInt('0'),
public size: number = 0.0,
public name: string|Uint8Array|null = null,
public resources: number = 0.0,
public capital: number = 0.0,
public material: number = 0.0,
public industry: number = 0.0,
public population: number = 0.0,
public colonists: number = 0.0,
public production: string|Uint8Array|null = null,
public freeIndustry: number = 0.0
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const owner = (this.owner !== null ? builder.createString(this.owner!) : 0);
const name = (this.name !== null ? builder.createString(this.name!) : 0);
const production = (this.production !== null ? builder.createString(this.production!) : 0);
return OtherPlanet.createOtherPlanet(builder,
owner,
this.x,
this.y,
this.number,
this.size,
name,
this.resources,
this.capital,
this.material,
this.industry,
this.population,
this.colonists,
production,
this.freeIndustry
);
}
}
@@ -0,0 +1,151 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class OtherScience implements flatbuffers.IUnpackableObject<OtherScienceT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):OtherScience {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsOtherScience(bb:flatbuffers.ByteBuffer, obj?:OtherScience):OtherScience {
return (obj || new OtherScience()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsOtherScience(bb:flatbuffers.ByteBuffer, obj?:OtherScience):OtherScience {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new OtherScience()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
race():string|null
race(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
race(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
name():string|null
name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
name(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
drive():number {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
weapons():number {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
shields():number {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
cargo():number {
const offset = this.bb!.__offset(this.bb_pos, 14);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
static startOtherScience(builder:flatbuffers.Builder) {
builder.startObject(6);
}
static addRace(builder:flatbuffers.Builder, raceOffset:flatbuffers.Offset) {
builder.addFieldOffset(0, raceOffset, 0);
}
static addName(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset) {
builder.addFieldOffset(1, nameOffset, 0);
}
static addDrive(builder:flatbuffers.Builder, drive:number) {
builder.addFieldFloat32(2, drive, 0.0);
}
static addWeapons(builder:flatbuffers.Builder, weapons:number) {
builder.addFieldFloat32(3, weapons, 0.0);
}
static addShields(builder:flatbuffers.Builder, shields:number) {
builder.addFieldFloat32(4, shields, 0.0);
}
static addCargo(builder:flatbuffers.Builder, cargo:number) {
builder.addFieldFloat32(5, cargo, 0.0);
}
static endOtherScience(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createOtherScience(builder:flatbuffers.Builder, raceOffset:flatbuffers.Offset, nameOffset:flatbuffers.Offset, drive:number, weapons:number, shields:number, cargo:number):flatbuffers.Offset {
OtherScience.startOtherScience(builder);
OtherScience.addRace(builder, raceOffset);
OtherScience.addName(builder, nameOffset);
OtherScience.addDrive(builder, drive);
OtherScience.addWeapons(builder, weapons);
OtherScience.addShields(builder, shields);
OtherScience.addCargo(builder, cargo);
return OtherScience.endOtherScience(builder);
}
unpack(): OtherScienceT {
return new OtherScienceT(
this.race(),
this.name(),
this.drive(),
this.weapons(),
this.shields(),
this.cargo()
);
}
unpackTo(_o: OtherScienceT): void {
_o.race = this.race();
_o.name = this.name();
_o.drive = this.drive();
_o.weapons = this.weapons();
_o.shields = this.shields();
_o.cargo = this.cargo();
}
}
export class OtherScienceT implements flatbuffers.IGeneratedObject {
constructor(
public race: string|Uint8Array|null = null,
public name: string|Uint8Array|null = null,
public drive: number = 0.0,
public weapons: number = 0.0,
public shields: number = 0.0,
public cargo: number = 0.0
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const race = (this.race !== null ? builder.createString(this.race!) : 0);
const name = (this.name !== null ? builder.createString(this.name!) : 0);
return OtherScience.createOtherScience(builder,
race,
name,
this.drive,
this.weapons,
this.shields,
this.cargo
);
}
}
@@ -0,0 +1,179 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class OthersShipClass implements flatbuffers.IUnpackableObject<OthersShipClassT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):OthersShipClass {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsOthersShipClass(bb:flatbuffers.ByteBuffer, obj?:OthersShipClass):OthersShipClass {
return (obj || new OthersShipClass()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsOthersShipClass(bb:flatbuffers.ByteBuffer, obj?:OthersShipClass):OthersShipClass {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new OthersShipClass()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
race():string|null
race(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
race(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
name():string|null
name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
name(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
drive():number {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
armament():bigint {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
weapons():number {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
shields():number {
const offset = this.bb!.__offset(this.bb_pos, 14);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
cargo():number {
const offset = this.bb!.__offset(this.bb_pos, 16);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
mass():number {
const offset = this.bb!.__offset(this.bb_pos, 18);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
static startOthersShipClass(builder:flatbuffers.Builder) {
builder.startObject(8);
}
static addRace(builder:flatbuffers.Builder, raceOffset:flatbuffers.Offset) {
builder.addFieldOffset(0, raceOffset, 0);
}
static addName(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset) {
builder.addFieldOffset(1, nameOffset, 0);
}
static addDrive(builder:flatbuffers.Builder, drive:number) {
builder.addFieldFloat32(2, drive, 0.0);
}
static addArmament(builder:flatbuffers.Builder, armament:bigint) {
builder.addFieldInt64(3, armament, BigInt('0'));
}
static addWeapons(builder:flatbuffers.Builder, weapons:number) {
builder.addFieldFloat32(4, weapons, 0.0);
}
static addShields(builder:flatbuffers.Builder, shields:number) {
builder.addFieldFloat32(5, shields, 0.0);
}
static addCargo(builder:flatbuffers.Builder, cargo:number) {
builder.addFieldFloat32(6, cargo, 0.0);
}
static addMass(builder:flatbuffers.Builder, mass:number) {
builder.addFieldFloat32(7, mass, 0.0);
}
static endOthersShipClass(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createOthersShipClass(builder:flatbuffers.Builder, raceOffset:flatbuffers.Offset, nameOffset:flatbuffers.Offset, drive:number, armament:bigint, weapons:number, shields:number, cargo:number, mass:number):flatbuffers.Offset {
OthersShipClass.startOthersShipClass(builder);
OthersShipClass.addRace(builder, raceOffset);
OthersShipClass.addName(builder, nameOffset);
OthersShipClass.addDrive(builder, drive);
OthersShipClass.addArmament(builder, armament);
OthersShipClass.addWeapons(builder, weapons);
OthersShipClass.addShields(builder, shields);
OthersShipClass.addCargo(builder, cargo);
OthersShipClass.addMass(builder, mass);
return OthersShipClass.endOthersShipClass(builder);
}
unpack(): OthersShipClassT {
return new OthersShipClassT(
this.race(),
this.name(),
this.drive(),
this.armament(),
this.weapons(),
this.shields(),
this.cargo(),
this.mass()
);
}
unpackTo(_o: OthersShipClassT): void {
_o.race = this.race();
_o.name = this.name();
_o.drive = this.drive();
_o.armament = this.armament();
_o.weapons = this.weapons();
_o.shields = this.shields();
_o.cargo = this.cargo();
_o.mass = this.mass();
}
}
export class OthersShipClassT implements flatbuffers.IGeneratedObject {
constructor(
public race: string|Uint8Array|null = null,
public name: string|Uint8Array|null = null,
public drive: number = 0.0,
public armament: bigint = BigInt('0'),
public weapons: number = 0.0,
public shields: number = 0.0,
public cargo: number = 0.0,
public mass: number = 0.0
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const race = (this.race !== null ? builder.createString(this.race!) : 0);
const name = (this.name !== null ? builder.createString(this.name!) : 0);
return OthersShipClass.createOthersShipClass(builder,
race,
name,
this.drive,
this.armament,
this.weapons,
this.shields,
this.cargo,
this.mass
);
}
}
@@ -0,0 +1,221 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class Player implements flatbuffers.IUnpackableObject<PlayerT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):Player {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsPlayer(bb:flatbuffers.ByteBuffer, obj?:Player):Player {
return (obj || new Player()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsPlayer(bb:flatbuffers.ByteBuffer, obj?:Player):Player {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new Player()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
name():string|null
name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
name(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
drive():number {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
weapons():number {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
shields():number {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
cargo():number {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
population():number {
const offset = this.bb!.__offset(this.bb_pos, 14);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
industry():number {
const offset = this.bb!.__offset(this.bb_pos, 16);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
planets():number {
const offset = this.bb!.__offset(this.bb_pos, 18);
return offset ? this.bb!.readUint16(this.bb_pos + offset) : 0;
}
relation():string|null
relation(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
relation(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 20);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
votes():number {
const offset = this.bb!.__offset(this.bb_pos, 22);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
extinct():boolean {
const offset = this.bb!.__offset(this.bb_pos, 24);
return offset ? !!this.bb!.readInt8(this.bb_pos + offset) : false;
}
static startPlayer(builder:flatbuffers.Builder) {
builder.startObject(11);
}
static addName(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset) {
builder.addFieldOffset(0, nameOffset, 0);
}
static addDrive(builder:flatbuffers.Builder, drive:number) {
builder.addFieldFloat32(1, drive, 0.0);
}
static addWeapons(builder:flatbuffers.Builder, weapons:number) {
builder.addFieldFloat32(2, weapons, 0.0);
}
static addShields(builder:flatbuffers.Builder, shields:number) {
builder.addFieldFloat32(3, shields, 0.0);
}
static addCargo(builder:flatbuffers.Builder, cargo:number) {
builder.addFieldFloat32(4, cargo, 0.0);
}
static addPopulation(builder:flatbuffers.Builder, population:number) {
builder.addFieldFloat32(5, population, 0.0);
}
static addIndustry(builder:flatbuffers.Builder, industry:number) {
builder.addFieldFloat32(6, industry, 0.0);
}
static addPlanets(builder:flatbuffers.Builder, planets:number) {
builder.addFieldInt16(7, planets, 0);
}
static addRelation(builder:flatbuffers.Builder, relationOffset:flatbuffers.Offset) {
builder.addFieldOffset(8, relationOffset, 0);
}
static addVotes(builder:flatbuffers.Builder, votes:number) {
builder.addFieldFloat32(9, votes, 0.0);
}
static addExtinct(builder:flatbuffers.Builder, extinct:boolean) {
builder.addFieldInt8(10, +extinct, +false);
}
static endPlayer(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createPlayer(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset, drive:number, weapons:number, shields:number, cargo:number, population:number, industry:number, planets:number, relationOffset:flatbuffers.Offset, votes:number, extinct:boolean):flatbuffers.Offset {
Player.startPlayer(builder);
Player.addName(builder, nameOffset);
Player.addDrive(builder, drive);
Player.addWeapons(builder, weapons);
Player.addShields(builder, shields);
Player.addCargo(builder, cargo);
Player.addPopulation(builder, population);
Player.addIndustry(builder, industry);
Player.addPlanets(builder, planets);
Player.addRelation(builder, relationOffset);
Player.addVotes(builder, votes);
Player.addExtinct(builder, extinct);
return Player.endPlayer(builder);
}
unpack(): PlayerT {
return new PlayerT(
this.name(),
this.drive(),
this.weapons(),
this.shields(),
this.cargo(),
this.population(),
this.industry(),
this.planets(),
this.relation(),
this.votes(),
this.extinct()
);
}
unpackTo(_o: PlayerT): void {
_o.name = this.name();
_o.drive = this.drive();
_o.weapons = this.weapons();
_o.shields = this.shields();
_o.cargo = this.cargo();
_o.population = this.population();
_o.industry = this.industry();
_o.planets = this.planets();
_o.relation = this.relation();
_o.votes = this.votes();
_o.extinct = this.extinct();
}
}
export class PlayerT implements flatbuffers.IGeneratedObject {
constructor(
public name: string|Uint8Array|null = null,
public drive: number = 0.0,
public weapons: number = 0.0,
public shields: number = 0.0,
public cargo: number = 0.0,
public population: number = 0.0,
public industry: number = 0.0,
public planets: number = 0,
public relation: string|Uint8Array|null = null,
public votes: number = 0.0,
public extinct: boolean = false
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const name = (this.name !== null ? builder.createString(this.name!) : 0);
const relation = (this.relation !== null ? builder.createString(this.relation!) : 0);
return Player.createPlayer(builder,
name,
this.drive,
this.weapons,
this.shields,
this.cargo,
this.population,
this.industry,
this.planets,
relation,
this.votes,
this.extinct
);
}
}
@@ -0,0 +1,773 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
import { UUID, UUIDT } from '../common/uuid.js';
import { Bombing, BombingT } from './bombing.js';
import { IncomingGroup, IncomingGroupT } from './incoming-group.js';
import { LocalFleet, LocalFleetT } from './local-fleet.js';
import { LocalGroup, LocalGroupT } from './local-group.js';
import { LocalPlanet, LocalPlanetT } from './local-planet.js';
import { OtherGroup, OtherGroupT } from './other-group.js';
import { OtherPlanet, OtherPlanetT } from './other-planet.js';
import { OtherScience, OtherScienceT } from './other-science.js';
import { OthersShipClass, OthersShipClassT } from './others-ship-class.js';
import { Player, PlayerT } from './player.js';
import { Route, RouteT } from './route.js';
import { Science, ScienceT } from './science.js';
import { ShipClass, ShipClassT } from './ship-class.js';
import { ShipProduction, ShipProductionT } from './ship-production.js';
import { UnidentifiedGroup, UnidentifiedGroupT } from './unidentified-group.js';
import { UnidentifiedPlanet, UnidentifiedPlanetT } from './unidentified-planet.js';
import { UninhabitedPlanet, UninhabitedPlanetT } from './uninhabited-planet.js';
export class Report implements flatbuffers.IUnpackableObject<ReportT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):Report {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsReport(bb:flatbuffers.ByteBuffer, obj?:Report):Report {
return (obj || new Report()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsReport(bb:flatbuffers.ByteBuffer, obj?:Report):Report {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new Report()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
version():bigint {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
turn():bigint {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
width():number {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.readUint32(this.bb_pos + offset) : 0;
}
height():number {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.readUint32(this.bb_pos + offset) : 0;
}
planetCount():number {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.readUint32(this.bb_pos + offset) : 0;
}
race():string|null
race(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
race(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 14);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
votes():number {
const offset = this.bb!.__offset(this.bb_pos, 16);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
voteFor():string|null
voteFor(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
voteFor(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 18);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
player(index: number, obj?:Player):Player|null {
const offset = this.bb!.__offset(this.bb_pos, 20);
return offset ? (obj || new Player()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
playerLength():number {
const offset = this.bb!.__offset(this.bb_pos, 20);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
localScience(index: number, obj?:Science):Science|null {
const offset = this.bb!.__offset(this.bb_pos, 22);
return offset ? (obj || new Science()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
localScienceLength():number {
const offset = this.bb!.__offset(this.bb_pos, 22);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
otherScience(index: number, obj?:OtherScience):OtherScience|null {
const offset = this.bb!.__offset(this.bb_pos, 24);
return offset ? (obj || new OtherScience()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
otherScienceLength():number {
const offset = this.bb!.__offset(this.bb_pos, 24);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
localShipClass(index: number, obj?:ShipClass):ShipClass|null {
const offset = this.bb!.__offset(this.bb_pos, 26);
return offset ? (obj || new ShipClass()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
localShipClassLength():number {
const offset = this.bb!.__offset(this.bb_pos, 26);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
otherShipClass(index: number, obj?:OthersShipClass):OthersShipClass|null {
const offset = this.bb!.__offset(this.bb_pos, 28);
return offset ? (obj || new OthersShipClass()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
otherShipClassLength():number {
const offset = this.bb!.__offset(this.bb_pos, 28);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
battle(index: number, obj?:UUID):UUID|null {
const offset = this.bb!.__offset(this.bb_pos, 30);
return offset ? (obj || new UUID()).__init(this.bb!.__vector(this.bb_pos + offset) + index * 16, this.bb!) : null;
}
battleLength():number {
const offset = this.bb!.__offset(this.bb_pos, 30);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
bombing(index: number, obj?:Bombing):Bombing|null {
const offset = this.bb!.__offset(this.bb_pos, 32);
return offset ? (obj || new Bombing()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
bombingLength():number {
const offset = this.bb!.__offset(this.bb_pos, 32);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
incomingGroup(index: number, obj?:IncomingGroup):IncomingGroup|null {
const offset = this.bb!.__offset(this.bb_pos, 34);
return offset ? (obj || new IncomingGroup()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
incomingGroupLength():number {
const offset = this.bb!.__offset(this.bb_pos, 34);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
localPlanet(index: number, obj?:LocalPlanet):LocalPlanet|null {
const offset = this.bb!.__offset(this.bb_pos, 36);
return offset ? (obj || new LocalPlanet()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
localPlanetLength():number {
const offset = this.bb!.__offset(this.bb_pos, 36);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
shipProduction(index: number, obj?:ShipProduction):ShipProduction|null {
const offset = this.bb!.__offset(this.bb_pos, 38);
return offset ? (obj || new ShipProduction()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
shipProductionLength():number {
const offset = this.bb!.__offset(this.bb_pos, 38);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
route(index: number, obj?:Route):Route|null {
const offset = this.bb!.__offset(this.bb_pos, 40);
return offset ? (obj || new Route()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
routeLength():number {
const offset = this.bb!.__offset(this.bb_pos, 40);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
otherPlanet(index: number, obj?:OtherPlanet):OtherPlanet|null {
const offset = this.bb!.__offset(this.bb_pos, 42);
return offset ? (obj || new OtherPlanet()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
otherPlanetLength():number {
const offset = this.bb!.__offset(this.bb_pos, 42);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
uninhabitedPlanet(index: number, obj?:UninhabitedPlanet):UninhabitedPlanet|null {
const offset = this.bb!.__offset(this.bb_pos, 44);
return offset ? (obj || new UninhabitedPlanet()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
uninhabitedPlanetLength():number {
const offset = this.bb!.__offset(this.bb_pos, 44);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
unidentifiedPlanet(index: number, obj?:UnidentifiedPlanet):UnidentifiedPlanet|null {
const offset = this.bb!.__offset(this.bb_pos, 46);
return offset ? (obj || new UnidentifiedPlanet()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
unidentifiedPlanetLength():number {
const offset = this.bb!.__offset(this.bb_pos, 46);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
localFleet(index: number, obj?:LocalFleet):LocalFleet|null {
const offset = this.bb!.__offset(this.bb_pos, 48);
return offset ? (obj || new LocalFleet()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
localFleetLength():number {
const offset = this.bb!.__offset(this.bb_pos, 48);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
localGroup(index: number, obj?:LocalGroup):LocalGroup|null {
const offset = this.bb!.__offset(this.bb_pos, 50);
return offset ? (obj || new LocalGroup()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
localGroupLength():number {
const offset = this.bb!.__offset(this.bb_pos, 50);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
otherGroup(index: number, obj?:OtherGroup):OtherGroup|null {
const offset = this.bb!.__offset(this.bb_pos, 52);
return offset ? (obj || new OtherGroup()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
otherGroupLength():number {
const offset = this.bb!.__offset(this.bb_pos, 52);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
unidentifiedGroup(index: number, obj?:UnidentifiedGroup):UnidentifiedGroup|null {
const offset = this.bb!.__offset(this.bb_pos, 54);
return offset ? (obj || new UnidentifiedGroup()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
unidentifiedGroupLength():number {
const offset = this.bb!.__offset(this.bb_pos, 54);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
static startReport(builder:flatbuffers.Builder) {
builder.startObject(26);
}
static addVersion(builder:flatbuffers.Builder, version:bigint) {
builder.addFieldInt64(0, version, BigInt('0'));
}
static addTurn(builder:flatbuffers.Builder, turn:bigint) {
builder.addFieldInt64(1, turn, BigInt('0'));
}
static addWidth(builder:flatbuffers.Builder, width:number) {
builder.addFieldInt32(2, width, 0);
}
static addHeight(builder:flatbuffers.Builder, height:number) {
builder.addFieldInt32(3, height, 0);
}
static addPlanetCount(builder:flatbuffers.Builder, planetCount:number) {
builder.addFieldInt32(4, planetCount, 0);
}
static addRace(builder:flatbuffers.Builder, raceOffset:flatbuffers.Offset) {
builder.addFieldOffset(5, raceOffset, 0);
}
static addVotes(builder:flatbuffers.Builder, votes:number) {
builder.addFieldFloat32(6, votes, 0.0);
}
static addVoteFor(builder:flatbuffers.Builder, voteForOffset:flatbuffers.Offset) {
builder.addFieldOffset(7, voteForOffset, 0);
}
static addPlayer(builder:flatbuffers.Builder, playerOffset:flatbuffers.Offset) {
builder.addFieldOffset(8, playerOffset, 0);
}
static createPlayerVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startPlayerVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addLocalScience(builder:flatbuffers.Builder, localScienceOffset:flatbuffers.Offset) {
builder.addFieldOffset(9, localScienceOffset, 0);
}
static createLocalScienceVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startLocalScienceVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addOtherScience(builder:flatbuffers.Builder, otherScienceOffset:flatbuffers.Offset) {
builder.addFieldOffset(10, otherScienceOffset, 0);
}
static createOtherScienceVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startOtherScienceVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addLocalShipClass(builder:flatbuffers.Builder, localShipClassOffset:flatbuffers.Offset) {
builder.addFieldOffset(11, localShipClassOffset, 0);
}
static createLocalShipClassVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startLocalShipClassVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addOtherShipClass(builder:flatbuffers.Builder, otherShipClassOffset:flatbuffers.Offset) {
builder.addFieldOffset(12, otherShipClassOffset, 0);
}
static createOtherShipClassVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startOtherShipClassVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addBattle(builder:flatbuffers.Builder, battleOffset:flatbuffers.Offset) {
builder.addFieldOffset(13, battleOffset, 0);
}
static startBattleVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(16, numElems, 8);
}
static addBombing(builder:flatbuffers.Builder, bombingOffset:flatbuffers.Offset) {
builder.addFieldOffset(14, bombingOffset, 0);
}
static createBombingVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startBombingVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addIncomingGroup(builder:flatbuffers.Builder, incomingGroupOffset:flatbuffers.Offset) {
builder.addFieldOffset(15, incomingGroupOffset, 0);
}
static createIncomingGroupVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startIncomingGroupVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addLocalPlanet(builder:flatbuffers.Builder, localPlanetOffset:flatbuffers.Offset) {
builder.addFieldOffset(16, localPlanetOffset, 0);
}
static createLocalPlanetVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startLocalPlanetVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addShipProduction(builder:flatbuffers.Builder, shipProductionOffset:flatbuffers.Offset) {
builder.addFieldOffset(17, shipProductionOffset, 0);
}
static createShipProductionVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startShipProductionVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addRoute(builder:flatbuffers.Builder, routeOffset:flatbuffers.Offset) {
builder.addFieldOffset(18, routeOffset, 0);
}
static createRouteVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startRouteVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addOtherPlanet(builder:flatbuffers.Builder, otherPlanetOffset:flatbuffers.Offset) {
builder.addFieldOffset(19, otherPlanetOffset, 0);
}
static createOtherPlanetVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startOtherPlanetVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addUninhabitedPlanet(builder:flatbuffers.Builder, uninhabitedPlanetOffset:flatbuffers.Offset) {
builder.addFieldOffset(20, uninhabitedPlanetOffset, 0);
}
static createUninhabitedPlanetVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startUninhabitedPlanetVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addUnidentifiedPlanet(builder:flatbuffers.Builder, unidentifiedPlanetOffset:flatbuffers.Offset) {
builder.addFieldOffset(21, unidentifiedPlanetOffset, 0);
}
static createUnidentifiedPlanetVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startUnidentifiedPlanetVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addLocalFleet(builder:flatbuffers.Builder, localFleetOffset:flatbuffers.Offset) {
builder.addFieldOffset(22, localFleetOffset, 0);
}
static createLocalFleetVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startLocalFleetVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addLocalGroup(builder:flatbuffers.Builder, localGroupOffset:flatbuffers.Offset) {
builder.addFieldOffset(23, localGroupOffset, 0);
}
static createLocalGroupVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startLocalGroupVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addOtherGroup(builder:flatbuffers.Builder, otherGroupOffset:flatbuffers.Offset) {
builder.addFieldOffset(24, otherGroupOffset, 0);
}
static createOtherGroupVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startOtherGroupVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static addUnidentifiedGroup(builder:flatbuffers.Builder, unidentifiedGroupOffset:flatbuffers.Offset) {
builder.addFieldOffset(25, unidentifiedGroupOffset, 0);
}
static createUnidentifiedGroupVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startUnidentifiedGroupVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static endReport(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static finishReportBuffer(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {
builder.finish(offset);
}
static finishSizePrefixedReportBuffer(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {
builder.finish(offset, undefined, true);
}
static createReport(builder:flatbuffers.Builder, version:bigint, turn:bigint, width:number, height:number, planetCount:number, raceOffset:flatbuffers.Offset, votes:number, voteForOffset:flatbuffers.Offset, playerOffset:flatbuffers.Offset, localScienceOffset:flatbuffers.Offset, otherScienceOffset:flatbuffers.Offset, localShipClassOffset:flatbuffers.Offset, otherShipClassOffset:flatbuffers.Offset, battleOffset:flatbuffers.Offset, bombingOffset:flatbuffers.Offset, incomingGroupOffset:flatbuffers.Offset, localPlanetOffset:flatbuffers.Offset, shipProductionOffset:flatbuffers.Offset, routeOffset:flatbuffers.Offset, otherPlanetOffset:flatbuffers.Offset, uninhabitedPlanetOffset:flatbuffers.Offset, unidentifiedPlanetOffset:flatbuffers.Offset, localFleetOffset:flatbuffers.Offset, localGroupOffset:flatbuffers.Offset, otherGroupOffset:flatbuffers.Offset, unidentifiedGroupOffset:flatbuffers.Offset):flatbuffers.Offset {
Report.startReport(builder);
Report.addVersion(builder, version);
Report.addTurn(builder, turn);
Report.addWidth(builder, width);
Report.addHeight(builder, height);
Report.addPlanetCount(builder, planetCount);
Report.addRace(builder, raceOffset);
Report.addVotes(builder, votes);
Report.addVoteFor(builder, voteForOffset);
Report.addPlayer(builder, playerOffset);
Report.addLocalScience(builder, localScienceOffset);
Report.addOtherScience(builder, otherScienceOffset);
Report.addLocalShipClass(builder, localShipClassOffset);
Report.addOtherShipClass(builder, otherShipClassOffset);
Report.addBattle(builder, battleOffset);
Report.addBombing(builder, bombingOffset);
Report.addIncomingGroup(builder, incomingGroupOffset);
Report.addLocalPlanet(builder, localPlanetOffset);
Report.addShipProduction(builder, shipProductionOffset);
Report.addRoute(builder, routeOffset);
Report.addOtherPlanet(builder, otherPlanetOffset);
Report.addUninhabitedPlanet(builder, uninhabitedPlanetOffset);
Report.addUnidentifiedPlanet(builder, unidentifiedPlanetOffset);
Report.addLocalFleet(builder, localFleetOffset);
Report.addLocalGroup(builder, localGroupOffset);
Report.addOtherGroup(builder, otherGroupOffset);
Report.addUnidentifiedGroup(builder, unidentifiedGroupOffset);
return Report.endReport(builder);
}
unpack(): ReportT {
return new ReportT(
this.version(),
this.turn(),
this.width(),
this.height(),
this.planetCount(),
this.race(),
this.votes(),
this.voteFor(),
this.bb!.createObjList<Player, PlayerT>(this.player.bind(this), this.playerLength()),
this.bb!.createObjList<Science, ScienceT>(this.localScience.bind(this), this.localScienceLength()),
this.bb!.createObjList<OtherScience, OtherScienceT>(this.otherScience.bind(this), this.otherScienceLength()),
this.bb!.createObjList<ShipClass, ShipClassT>(this.localShipClass.bind(this), this.localShipClassLength()),
this.bb!.createObjList<OthersShipClass, OthersShipClassT>(this.otherShipClass.bind(this), this.otherShipClassLength()),
this.bb!.createObjList<UUID, UUIDT>(this.battle.bind(this), this.battleLength()),
this.bb!.createObjList<Bombing, BombingT>(this.bombing.bind(this), this.bombingLength()),
this.bb!.createObjList<IncomingGroup, IncomingGroupT>(this.incomingGroup.bind(this), this.incomingGroupLength()),
this.bb!.createObjList<LocalPlanet, LocalPlanetT>(this.localPlanet.bind(this), this.localPlanetLength()),
this.bb!.createObjList<ShipProduction, ShipProductionT>(this.shipProduction.bind(this), this.shipProductionLength()),
this.bb!.createObjList<Route, RouteT>(this.route.bind(this), this.routeLength()),
this.bb!.createObjList<OtherPlanet, OtherPlanetT>(this.otherPlanet.bind(this), this.otherPlanetLength()),
this.bb!.createObjList<UninhabitedPlanet, UninhabitedPlanetT>(this.uninhabitedPlanet.bind(this), this.uninhabitedPlanetLength()),
this.bb!.createObjList<UnidentifiedPlanet, UnidentifiedPlanetT>(this.unidentifiedPlanet.bind(this), this.unidentifiedPlanetLength()),
this.bb!.createObjList<LocalFleet, LocalFleetT>(this.localFleet.bind(this), this.localFleetLength()),
this.bb!.createObjList<LocalGroup, LocalGroupT>(this.localGroup.bind(this), this.localGroupLength()),
this.bb!.createObjList<OtherGroup, OtherGroupT>(this.otherGroup.bind(this), this.otherGroupLength()),
this.bb!.createObjList<UnidentifiedGroup, UnidentifiedGroupT>(this.unidentifiedGroup.bind(this), this.unidentifiedGroupLength())
);
}
unpackTo(_o: ReportT): void {
_o.version = this.version();
_o.turn = this.turn();
_o.width = this.width();
_o.height = this.height();
_o.planetCount = this.planetCount();
_o.race = this.race();
_o.votes = this.votes();
_o.voteFor = this.voteFor();
_o.player = this.bb!.createObjList<Player, PlayerT>(this.player.bind(this), this.playerLength());
_o.localScience = this.bb!.createObjList<Science, ScienceT>(this.localScience.bind(this), this.localScienceLength());
_o.otherScience = this.bb!.createObjList<OtherScience, OtherScienceT>(this.otherScience.bind(this), this.otherScienceLength());
_o.localShipClass = this.bb!.createObjList<ShipClass, ShipClassT>(this.localShipClass.bind(this), this.localShipClassLength());
_o.otherShipClass = this.bb!.createObjList<OthersShipClass, OthersShipClassT>(this.otherShipClass.bind(this), this.otherShipClassLength());
_o.battle = this.bb!.createObjList<UUID, UUIDT>(this.battle.bind(this), this.battleLength());
_o.bombing = this.bb!.createObjList<Bombing, BombingT>(this.bombing.bind(this), this.bombingLength());
_o.incomingGroup = this.bb!.createObjList<IncomingGroup, IncomingGroupT>(this.incomingGroup.bind(this), this.incomingGroupLength());
_o.localPlanet = this.bb!.createObjList<LocalPlanet, LocalPlanetT>(this.localPlanet.bind(this), this.localPlanetLength());
_o.shipProduction = this.bb!.createObjList<ShipProduction, ShipProductionT>(this.shipProduction.bind(this), this.shipProductionLength());
_o.route = this.bb!.createObjList<Route, RouteT>(this.route.bind(this), this.routeLength());
_o.otherPlanet = this.bb!.createObjList<OtherPlanet, OtherPlanetT>(this.otherPlanet.bind(this), this.otherPlanetLength());
_o.uninhabitedPlanet = this.bb!.createObjList<UninhabitedPlanet, UninhabitedPlanetT>(this.uninhabitedPlanet.bind(this), this.uninhabitedPlanetLength());
_o.unidentifiedPlanet = this.bb!.createObjList<UnidentifiedPlanet, UnidentifiedPlanetT>(this.unidentifiedPlanet.bind(this), this.unidentifiedPlanetLength());
_o.localFleet = this.bb!.createObjList<LocalFleet, LocalFleetT>(this.localFleet.bind(this), this.localFleetLength());
_o.localGroup = this.bb!.createObjList<LocalGroup, LocalGroupT>(this.localGroup.bind(this), this.localGroupLength());
_o.otherGroup = this.bb!.createObjList<OtherGroup, OtherGroupT>(this.otherGroup.bind(this), this.otherGroupLength());
_o.unidentifiedGroup = this.bb!.createObjList<UnidentifiedGroup, UnidentifiedGroupT>(this.unidentifiedGroup.bind(this), this.unidentifiedGroupLength());
}
}
export class ReportT implements flatbuffers.IGeneratedObject {
constructor(
public version: bigint = BigInt('0'),
public turn: bigint = BigInt('0'),
public width: number = 0,
public height: number = 0,
public planetCount: number = 0,
public race: string|Uint8Array|null = null,
public votes: number = 0.0,
public voteFor: string|Uint8Array|null = null,
public player: (PlayerT)[] = [],
public localScience: (ScienceT)[] = [],
public otherScience: (OtherScienceT)[] = [],
public localShipClass: (ShipClassT)[] = [],
public otherShipClass: (OthersShipClassT)[] = [],
public battle: (UUIDT)[] = [],
public bombing: (BombingT)[] = [],
public incomingGroup: (IncomingGroupT)[] = [],
public localPlanet: (LocalPlanetT)[] = [],
public shipProduction: (ShipProductionT)[] = [],
public route: (RouteT)[] = [],
public otherPlanet: (OtherPlanetT)[] = [],
public uninhabitedPlanet: (UninhabitedPlanetT)[] = [],
public unidentifiedPlanet: (UnidentifiedPlanetT)[] = [],
public localFleet: (LocalFleetT)[] = [],
public localGroup: (LocalGroupT)[] = [],
public otherGroup: (OtherGroupT)[] = [],
public unidentifiedGroup: (UnidentifiedGroupT)[] = []
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const race = (this.race !== null ? builder.createString(this.race!) : 0);
const voteFor = (this.voteFor !== null ? builder.createString(this.voteFor!) : 0);
const player = Report.createPlayerVector(builder, builder.createObjectOffsetList(this.player));
const localScience = Report.createLocalScienceVector(builder, builder.createObjectOffsetList(this.localScience));
const otherScience = Report.createOtherScienceVector(builder, builder.createObjectOffsetList(this.otherScience));
const localShipClass = Report.createLocalShipClassVector(builder, builder.createObjectOffsetList(this.localShipClass));
const otherShipClass = Report.createOtherShipClassVector(builder, builder.createObjectOffsetList(this.otherShipClass));
const battle = builder.createStructOffsetList(this.battle, Report.startBattleVector);
const bombing = Report.createBombingVector(builder, builder.createObjectOffsetList(this.bombing));
const incomingGroup = Report.createIncomingGroupVector(builder, builder.createObjectOffsetList(this.incomingGroup));
const localPlanet = Report.createLocalPlanetVector(builder, builder.createObjectOffsetList(this.localPlanet));
const shipProduction = Report.createShipProductionVector(builder, builder.createObjectOffsetList(this.shipProduction));
const route = Report.createRouteVector(builder, builder.createObjectOffsetList(this.route));
const otherPlanet = Report.createOtherPlanetVector(builder, builder.createObjectOffsetList(this.otherPlanet));
const uninhabitedPlanet = Report.createUninhabitedPlanetVector(builder, builder.createObjectOffsetList(this.uninhabitedPlanet));
const unidentifiedPlanet = Report.createUnidentifiedPlanetVector(builder, builder.createObjectOffsetList(this.unidentifiedPlanet));
const localFleet = Report.createLocalFleetVector(builder, builder.createObjectOffsetList(this.localFleet));
const localGroup = Report.createLocalGroupVector(builder, builder.createObjectOffsetList(this.localGroup));
const otherGroup = Report.createOtherGroupVector(builder, builder.createObjectOffsetList(this.otherGroup));
const unidentifiedGroup = Report.createUnidentifiedGroupVector(builder, builder.createObjectOffsetList(this.unidentifiedGroup));
return Report.createReport(builder,
this.version,
this.turn,
this.width,
this.height,
this.planetCount,
race,
this.votes,
voteFor,
player,
localScience,
otherScience,
localShipClass,
otherShipClass,
battle,
bombing,
incomingGroup,
localPlanet,
shipProduction,
route,
otherPlanet,
uninhabitedPlanet,
unidentifiedPlanet,
localFleet,
localGroup,
otherGroup,
unidentifiedGroup
);
}
}
@@ -0,0 +1,92 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class RouteEntry implements flatbuffers.IUnpackableObject<RouteEntryT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):RouteEntry {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsRouteEntry(bb:flatbuffers.ByteBuffer, obj?:RouteEntry):RouteEntry {
return (obj || new RouteEntry()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsRouteEntry(bb:flatbuffers.ByteBuffer, obj?:RouteEntry):RouteEntry {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new RouteEntry()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
key():bigint {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
value():string|null
value(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
value(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
static startRouteEntry(builder:flatbuffers.Builder) {
builder.startObject(2);
}
static addKey(builder:flatbuffers.Builder, key:bigint) {
builder.addFieldInt64(0, key, BigInt('0'));
}
static addValue(builder:flatbuffers.Builder, valueOffset:flatbuffers.Offset) {
builder.addFieldOffset(1, valueOffset, 0);
}
static endRouteEntry(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createRouteEntry(builder:flatbuffers.Builder, key:bigint, valueOffset:flatbuffers.Offset):flatbuffers.Offset {
RouteEntry.startRouteEntry(builder);
RouteEntry.addKey(builder, key);
RouteEntry.addValue(builder, valueOffset);
return RouteEntry.endRouteEntry(builder);
}
unpack(): RouteEntryT {
return new RouteEntryT(
this.key(),
this.value()
);
}
unpackTo(_o: RouteEntryT): void {
_o.key = this.key();
_o.value = this.value();
}
}
export class RouteEntryT implements flatbuffers.IGeneratedObject {
constructor(
public key: bigint = BigInt('0'),
public value: string|Uint8Array|null = null
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const value = (this.value !== null ? builder.createString(this.value!) : 0);
return RouteEntry.createRouteEntry(builder,
this.key,
value
);
}
}
@@ -0,0 +1,108 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
import { RouteEntry, RouteEntryT } from './route-entry.js';
export class Route implements flatbuffers.IUnpackableObject<RouteT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):Route {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsRoute(bb:flatbuffers.ByteBuffer, obj?:Route):Route {
return (obj || new Route()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsRoute(bb:flatbuffers.ByteBuffer, obj?:Route):Route {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new Route()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
planet():bigint {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
route(index: number, obj?:RouteEntry):RouteEntry|null {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? (obj || new RouteEntry()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null;
}
routeLength():number {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
}
static startRoute(builder:flatbuffers.Builder) {
builder.startObject(2);
}
static addPlanet(builder:flatbuffers.Builder, planet:bigint) {
builder.addFieldInt64(0, planet, BigInt('0'));
}
static addRoute(builder:flatbuffers.Builder, routeOffset:flatbuffers.Offset) {
builder.addFieldOffset(1, routeOffset, 0);
}
static createRouteVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset {
builder.startVector(4, data.length, 4);
for (let i = data.length - 1; i >= 0; i--) {
builder.addOffset(data[i]!);
}
return builder.endVector();
}
static startRouteVector(builder:flatbuffers.Builder, numElems:number) {
builder.startVector(4, numElems, 4);
}
static endRoute(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createRoute(builder:flatbuffers.Builder, planet:bigint, routeOffset:flatbuffers.Offset):flatbuffers.Offset {
Route.startRoute(builder);
Route.addPlanet(builder, planet);
Route.addRoute(builder, routeOffset);
return Route.endRoute(builder);
}
unpack(): RouteT {
return new RouteT(
this.planet(),
this.bb!.createObjList<RouteEntry, RouteEntryT>(this.route.bind(this), this.routeLength())
);
}
unpackTo(_o: RouteT): void {
_o.planet = this.planet();
_o.route = this.bb!.createObjList<RouteEntry, RouteEntryT>(this.route.bind(this), this.routeLength());
}
}
export class RouteT implements flatbuffers.IGeneratedObject {
constructor(
public planet: bigint = BigInt('0'),
public route: (RouteEntryT)[] = []
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const route = Route.createRouteVector(builder, builder.createObjectOffsetList(this.route));
return Route.createRoute(builder,
this.planet,
route
);
}
}
@@ -0,0 +1,134 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class Science implements flatbuffers.IUnpackableObject<ScienceT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):Science {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsScience(bb:flatbuffers.ByteBuffer, obj?:Science):Science {
return (obj || new Science()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsScience(bb:flatbuffers.ByteBuffer, obj?:Science):Science {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new Science()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
name():string|null
name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
name(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
drive():number {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
weapons():number {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
shields():number {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
cargo():number {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
static startScience(builder:flatbuffers.Builder) {
builder.startObject(5);
}
static addName(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset) {
builder.addFieldOffset(0, nameOffset, 0);
}
static addDrive(builder:flatbuffers.Builder, drive:number) {
builder.addFieldFloat32(1, drive, 0.0);
}
static addWeapons(builder:flatbuffers.Builder, weapons:number) {
builder.addFieldFloat32(2, weapons, 0.0);
}
static addShields(builder:flatbuffers.Builder, shields:number) {
builder.addFieldFloat32(3, shields, 0.0);
}
static addCargo(builder:flatbuffers.Builder, cargo:number) {
builder.addFieldFloat32(4, cargo, 0.0);
}
static endScience(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createScience(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset, drive:number, weapons:number, shields:number, cargo:number):flatbuffers.Offset {
Science.startScience(builder);
Science.addName(builder, nameOffset);
Science.addDrive(builder, drive);
Science.addWeapons(builder, weapons);
Science.addShields(builder, shields);
Science.addCargo(builder, cargo);
return Science.endScience(builder);
}
unpack(): ScienceT {
return new ScienceT(
this.name(),
this.drive(),
this.weapons(),
this.shields(),
this.cargo()
);
}
unpackTo(_o: ScienceT): void {
_o.name = this.name();
_o.drive = this.drive();
_o.weapons = this.weapons();
_o.shields = this.shields();
_o.cargo = this.cargo();
}
}
export class ScienceT implements flatbuffers.IGeneratedObject {
constructor(
public name: string|Uint8Array|null = null,
public drive: number = 0.0,
public weapons: number = 0.0,
public shields: number = 0.0,
public cargo: number = 0.0
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const name = (this.name !== null ? builder.createString(this.name!) : 0);
return Science.createScience(builder,
name,
this.drive,
this.weapons,
this.shields,
this.cargo
);
}
}
@@ -0,0 +1,162 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class ShipClass implements flatbuffers.IUnpackableObject<ShipClassT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):ShipClass {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsShipClass(bb:flatbuffers.ByteBuffer, obj?:ShipClass):ShipClass {
return (obj || new ShipClass()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsShipClass(bb:flatbuffers.ByteBuffer, obj?:ShipClass):ShipClass {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new ShipClass()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
name():string|null
name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
name(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
drive():number {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
armament():bigint {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
weapons():number {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
shields():number {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
cargo():number {
const offset = this.bb!.__offset(this.bb_pos, 14);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
mass():number {
const offset = this.bb!.__offset(this.bb_pos, 16);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
static startShipClass(builder:flatbuffers.Builder) {
builder.startObject(7);
}
static addName(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset) {
builder.addFieldOffset(0, nameOffset, 0);
}
static addDrive(builder:flatbuffers.Builder, drive:number) {
builder.addFieldFloat32(1, drive, 0.0);
}
static addArmament(builder:flatbuffers.Builder, armament:bigint) {
builder.addFieldInt64(2, armament, BigInt('0'));
}
static addWeapons(builder:flatbuffers.Builder, weapons:number) {
builder.addFieldFloat32(3, weapons, 0.0);
}
static addShields(builder:flatbuffers.Builder, shields:number) {
builder.addFieldFloat32(4, shields, 0.0);
}
static addCargo(builder:flatbuffers.Builder, cargo:number) {
builder.addFieldFloat32(5, cargo, 0.0);
}
static addMass(builder:flatbuffers.Builder, mass:number) {
builder.addFieldFloat32(6, mass, 0.0);
}
static endShipClass(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createShipClass(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset, drive:number, armament:bigint, weapons:number, shields:number, cargo:number, mass:number):flatbuffers.Offset {
ShipClass.startShipClass(builder);
ShipClass.addName(builder, nameOffset);
ShipClass.addDrive(builder, drive);
ShipClass.addArmament(builder, armament);
ShipClass.addWeapons(builder, weapons);
ShipClass.addShields(builder, shields);
ShipClass.addCargo(builder, cargo);
ShipClass.addMass(builder, mass);
return ShipClass.endShipClass(builder);
}
unpack(): ShipClassT {
return new ShipClassT(
this.name(),
this.drive(),
this.armament(),
this.weapons(),
this.shields(),
this.cargo(),
this.mass()
);
}
unpackTo(_o: ShipClassT): void {
_o.name = this.name();
_o.drive = this.drive();
_o.armament = this.armament();
_o.weapons = this.weapons();
_o.shields = this.shields();
_o.cargo = this.cargo();
_o.mass = this.mass();
}
}
export class ShipClassT implements flatbuffers.IGeneratedObject {
constructor(
public name: string|Uint8Array|null = null,
public drive: number = 0.0,
public armament: bigint = BigInt('0'),
public weapons: number = 0.0,
public shields: number = 0.0,
public cargo: number = 0.0,
public mass: number = 0.0
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const name = (this.name !== null ? builder.createString(this.name!) : 0);
return ShipClass.createShipClass(builder,
name,
this.drive,
this.armament,
this.weapons,
this.shields,
this.cargo,
this.mass
);
}
}
@@ -0,0 +1,148 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class ShipProduction implements flatbuffers.IUnpackableObject<ShipProductionT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):ShipProduction {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsShipProduction(bb:flatbuffers.ByteBuffer, obj?:ShipProduction):ShipProduction {
return (obj || new ShipProduction()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsShipProduction(bb:flatbuffers.ByteBuffer, obj?:ShipProduction):ShipProduction {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new ShipProduction()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
planet():bigint {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
class_():string|null
class_(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
class_(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
cost():number {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
prodUsed():number {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
percent():number {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
free():number {
const offset = this.bb!.__offset(this.bb_pos, 14);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
static startShipProduction(builder:flatbuffers.Builder) {
builder.startObject(6);
}
static addPlanet(builder:flatbuffers.Builder, planet:bigint) {
builder.addFieldInt64(0, planet, BigInt('0'));
}
static addClass(builder:flatbuffers.Builder, class_Offset:flatbuffers.Offset) {
builder.addFieldOffset(1, class_Offset, 0);
}
static addCost(builder:flatbuffers.Builder, cost:number) {
builder.addFieldFloat32(2, cost, 0.0);
}
static addProdUsed(builder:flatbuffers.Builder, prodUsed:number) {
builder.addFieldFloat32(3, prodUsed, 0.0);
}
static addPercent(builder:flatbuffers.Builder, percent:number) {
builder.addFieldFloat32(4, percent, 0.0);
}
static addFree(builder:flatbuffers.Builder, free:number) {
builder.addFieldFloat32(5, free, 0.0);
}
static endShipProduction(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createShipProduction(builder:flatbuffers.Builder, planet:bigint, class_Offset:flatbuffers.Offset, cost:number, prodUsed:number, percent:number, free:number):flatbuffers.Offset {
ShipProduction.startShipProduction(builder);
ShipProduction.addPlanet(builder, planet);
ShipProduction.addClass(builder, class_Offset);
ShipProduction.addCost(builder, cost);
ShipProduction.addProdUsed(builder, prodUsed);
ShipProduction.addPercent(builder, percent);
ShipProduction.addFree(builder, free);
return ShipProduction.endShipProduction(builder);
}
unpack(): ShipProductionT {
return new ShipProductionT(
this.planet(),
this.class_(),
this.cost(),
this.prodUsed(),
this.percent(),
this.free()
);
}
unpackTo(_o: ShipProductionT): void {
_o.planet = this.planet();
_o.class_ = this.class_();
_o.cost = this.cost();
_o.prodUsed = this.prodUsed();
_o.percent = this.percent();
_o.free = this.free();
}
}
export class ShipProductionT implements flatbuffers.IGeneratedObject {
constructor(
public planet: bigint = BigInt('0'),
public class_: string|Uint8Array|null = null,
public cost: number = 0.0,
public prodUsed: number = 0.0,
public percent: number = 0.0,
public free: number = 0.0
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const class_ = (this.class_ !== null ? builder.createString(this.class_!) : 0);
return ShipProduction.createShipProduction(builder,
this.planet,
class_,
this.cost,
this.prodUsed,
this.percent,
this.free
);
}
}
@@ -0,0 +1,92 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class TechEntry implements flatbuffers.IUnpackableObject<TechEntryT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):TechEntry {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsTechEntry(bb:flatbuffers.ByteBuffer, obj?:TechEntry):TechEntry {
return (obj || new TechEntry()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsTechEntry(bb:flatbuffers.ByteBuffer, obj?:TechEntry):TechEntry {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new TechEntry()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
key():string|null
key(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
key(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
value():number {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
static startTechEntry(builder:flatbuffers.Builder) {
builder.startObject(2);
}
static addKey(builder:flatbuffers.Builder, keyOffset:flatbuffers.Offset) {
builder.addFieldOffset(0, keyOffset, 0);
}
static addValue(builder:flatbuffers.Builder, value:number) {
builder.addFieldFloat32(1, value, 0.0);
}
static endTechEntry(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createTechEntry(builder:flatbuffers.Builder, keyOffset:flatbuffers.Offset, value:number):flatbuffers.Offset {
TechEntry.startTechEntry(builder);
TechEntry.addKey(builder, keyOffset);
TechEntry.addValue(builder, value);
return TechEntry.endTechEntry(builder);
}
unpack(): TechEntryT {
return new TechEntryT(
this.key(),
this.value()
);
}
unpackTo(_o: TechEntryT): void {
_o.key = this.key();
_o.value = this.value();
}
}
export class TechEntryT implements flatbuffers.IGeneratedObject {
constructor(
public key: string|Uint8Array|null = null,
public value: number = 0.0
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const key = (this.key !== null ? builder.createString(this.key!) : 0);
return TechEntry.createTechEntry(builder,
key,
this.value
);
}
}
@@ -0,0 +1,88 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class UnidentifiedGroup implements flatbuffers.IUnpackableObject<UnidentifiedGroupT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):UnidentifiedGroup {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsUnidentifiedGroup(bb:flatbuffers.ByteBuffer, obj?:UnidentifiedGroup):UnidentifiedGroup {
return (obj || new UnidentifiedGroup()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsUnidentifiedGroup(bb:flatbuffers.ByteBuffer, obj?:UnidentifiedGroup):UnidentifiedGroup {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new UnidentifiedGroup()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
x():number {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
y():number {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
static startUnidentifiedGroup(builder:flatbuffers.Builder) {
builder.startObject(2);
}
static addX(builder:flatbuffers.Builder, x:number) {
builder.addFieldFloat32(0, x, 0.0);
}
static addY(builder:flatbuffers.Builder, y:number) {
builder.addFieldFloat32(1, y, 0.0);
}
static endUnidentifiedGroup(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createUnidentifiedGroup(builder:flatbuffers.Builder, x:number, y:number):flatbuffers.Offset {
UnidentifiedGroup.startUnidentifiedGroup(builder);
UnidentifiedGroup.addX(builder, x);
UnidentifiedGroup.addY(builder, y);
return UnidentifiedGroup.endUnidentifiedGroup(builder);
}
unpack(): UnidentifiedGroupT {
return new UnidentifiedGroupT(
this.x(),
this.y()
);
}
unpackTo(_o: UnidentifiedGroupT): void {
_o.x = this.x();
_o.y = this.y();
}
}
export class UnidentifiedGroupT implements flatbuffers.IGeneratedObject {
constructor(
public x: number = 0.0,
public y: number = 0.0
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
return UnidentifiedGroup.createUnidentifiedGroup(builder,
this.x,
this.y
);
}
}
@@ -0,0 +1,102 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class UnidentifiedPlanet implements flatbuffers.IUnpackableObject<UnidentifiedPlanetT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):UnidentifiedPlanet {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsUnidentifiedPlanet(bb:flatbuffers.ByteBuffer, obj?:UnidentifiedPlanet):UnidentifiedPlanet {
return (obj || new UnidentifiedPlanet()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsUnidentifiedPlanet(bb:flatbuffers.ByteBuffer, obj?:UnidentifiedPlanet):UnidentifiedPlanet {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new UnidentifiedPlanet()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
x():number {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
y():number {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
number():bigint {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
static startUnidentifiedPlanet(builder:flatbuffers.Builder) {
builder.startObject(3);
}
static addX(builder:flatbuffers.Builder, x:number) {
builder.addFieldFloat32(0, x, 0.0);
}
static addY(builder:flatbuffers.Builder, y:number) {
builder.addFieldFloat32(1, y, 0.0);
}
static addNumber(builder:flatbuffers.Builder, number:bigint) {
builder.addFieldInt64(2, number, BigInt('0'));
}
static endUnidentifiedPlanet(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createUnidentifiedPlanet(builder:flatbuffers.Builder, x:number, y:number, number:bigint):flatbuffers.Offset {
UnidentifiedPlanet.startUnidentifiedPlanet(builder);
UnidentifiedPlanet.addX(builder, x);
UnidentifiedPlanet.addY(builder, y);
UnidentifiedPlanet.addNumber(builder, number);
return UnidentifiedPlanet.endUnidentifiedPlanet(builder);
}
unpack(): UnidentifiedPlanetT {
return new UnidentifiedPlanetT(
this.x(),
this.y(),
this.number()
);
}
unpackTo(_o: UnidentifiedPlanetT): void {
_o.x = this.x();
_o.y = this.y();
_o.number = this.number();
}
}
export class UnidentifiedPlanetT implements flatbuffers.IGeneratedObject {
constructor(
public x: number = 0.0,
public y: number = 0.0,
public number: bigint = BigInt('0')
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
return UnidentifiedPlanet.createUnidentifiedPlanet(builder,
this.x,
this.y,
this.number
);
}
}
@@ -0,0 +1,176 @@
// automatically generated by the FlatBuffers compiler, do not modify
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
import * as flatbuffers from 'flatbuffers';
export class UninhabitedPlanet implements flatbuffers.IUnpackableObject<UninhabitedPlanetT> {
bb: flatbuffers.ByteBuffer|null = null;
bb_pos = 0;
__init(i:number, bb:flatbuffers.ByteBuffer):UninhabitedPlanet {
this.bb_pos = i;
this.bb = bb;
return this;
}
static getRootAsUninhabitedPlanet(bb:flatbuffers.ByteBuffer, obj?:UninhabitedPlanet):UninhabitedPlanet {
return (obj || new UninhabitedPlanet()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
static getSizePrefixedRootAsUninhabitedPlanet(bb:flatbuffers.ByteBuffer, obj?:UninhabitedPlanet):UninhabitedPlanet {
bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH);
return (obj || new UninhabitedPlanet()).__init(bb.readInt32(bb.position()) + bb.position(), bb);
}
x():number {
const offset = this.bb!.__offset(this.bb_pos, 4);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
y():number {
const offset = this.bb!.__offset(this.bb_pos, 6);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
number():bigint {
const offset = this.bb!.__offset(this.bb_pos, 8);
return offset ? this.bb!.readUint64(this.bb_pos + offset) : BigInt('0');
}
size():number {
const offset = this.bb!.__offset(this.bb_pos, 10);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
name():string|null
name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
name(optionalEncoding?:any):string|Uint8Array|null {
const offset = this.bb!.__offset(this.bb_pos, 12);
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
}
resources():number {
const offset = this.bb!.__offset(this.bb_pos, 14);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
capital():number {
const offset = this.bb!.__offset(this.bb_pos, 16);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
material():number {
const offset = this.bb!.__offset(this.bb_pos, 18);
return offset ? this.bb!.readFloat32(this.bb_pos + offset) : 0.0;
}
static startUninhabitedPlanet(builder:flatbuffers.Builder) {
builder.startObject(8);
}
static addX(builder:flatbuffers.Builder, x:number) {
builder.addFieldFloat32(0, x, 0.0);
}
static addY(builder:flatbuffers.Builder, y:number) {
builder.addFieldFloat32(1, y, 0.0);
}
static addNumber(builder:flatbuffers.Builder, number:bigint) {
builder.addFieldInt64(2, number, BigInt('0'));
}
static addSize(builder:flatbuffers.Builder, size:number) {
builder.addFieldFloat32(3, size, 0.0);
}
static addName(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset) {
builder.addFieldOffset(4, nameOffset, 0);
}
static addResources(builder:flatbuffers.Builder, resources:number) {
builder.addFieldFloat32(5, resources, 0.0);
}
static addCapital(builder:flatbuffers.Builder, capital:number) {
builder.addFieldFloat32(6, capital, 0.0);
}
static addMaterial(builder:flatbuffers.Builder, material:number) {
builder.addFieldFloat32(7, material, 0.0);
}
static endUninhabitedPlanet(builder:flatbuffers.Builder):flatbuffers.Offset {
const offset = builder.endObject();
return offset;
}
static createUninhabitedPlanet(builder:flatbuffers.Builder, x:number, y:number, number:bigint, size:number, nameOffset:flatbuffers.Offset, resources:number, capital:number, material:number):flatbuffers.Offset {
UninhabitedPlanet.startUninhabitedPlanet(builder);
UninhabitedPlanet.addX(builder, x);
UninhabitedPlanet.addY(builder, y);
UninhabitedPlanet.addNumber(builder, number);
UninhabitedPlanet.addSize(builder, size);
UninhabitedPlanet.addName(builder, nameOffset);
UninhabitedPlanet.addResources(builder, resources);
UninhabitedPlanet.addCapital(builder, capital);
UninhabitedPlanet.addMaterial(builder, material);
return UninhabitedPlanet.endUninhabitedPlanet(builder);
}
unpack(): UninhabitedPlanetT {
return new UninhabitedPlanetT(
this.x(),
this.y(),
this.number(),
this.size(),
this.name(),
this.resources(),
this.capital(),
this.material()
);
}
unpackTo(_o: UninhabitedPlanetT): void {
_o.x = this.x();
_o.y = this.y();
_o.number = this.number();
_o.size = this.size();
_o.name = this.name();
_o.resources = this.resources();
_o.capital = this.capital();
_o.material = this.material();
}
}
export class UninhabitedPlanetT implements flatbuffers.IGeneratedObject {
constructor(
public x: number = 0.0,
public y: number = 0.0,
public number: bigint = BigInt('0'),
public size: number = 0.0,
public name: string|Uint8Array|null = null,
public resources: number = 0.0,
public capital: number = 0.0,
public material: number = 0.0
){}
pack(builder:flatbuffers.Builder): flatbuffers.Offset {
const name = (this.name !== null ? builder.createString(this.name!) : 0);
return UninhabitedPlanet.createUninhabitedPlanet(builder,
this.x,
this.y,
this.number,
this.size,
name,
this.resources,
this.capital,
this.material
);
}
}
@@ -12,11 +12,20 @@ layout owns:
header view-menu naturally drops the overlay even if `mobileTool` header view-menu naturally drops the overlay even if `mobileTool`
was set on a previous tap. was set on a previous tap.
Phase 11 adds the per-game `GameStateStore` instance owned by this
layout: it constructs the `GalaxyClient`, fetches the matching lobby
record to discover `current_turn`, then loads the report. The store
is shared with descendants via `setContext("gameState", ...)` so the
header turn counter, the map view, and later inspector tabs all read
from the same snapshot.
State preservation across active-view switches works for free State preservation across active-view switches works for free
because SvelteKit keeps this layout instance mounted while children because SvelteKit keeps this layout instance mounted while children
swap. swap; navigating between games unmounts and remounts the layout, so
the next game's snapshot is loaded fresh.
--> -->
<script lang="ts"> <script lang="ts">
import { onDestroy, onMount, setContext } from "svelte";
import { page } from "$app/state"; import { page } from "$app/state";
import Header from "$lib/header/header.svelte"; import Header from "$lib/header/header.svelte";
import Sidebar from "$lib/sidebar/sidebar.svelte"; import Sidebar from "$lib/sidebar/sidebar.svelte";
@@ -24,6 +33,13 @@ swap.
import Calculator from "$lib/sidebar/calculator-tab.svelte"; import Calculator from "$lib/sidebar/calculator-tab.svelte";
import Order from "$lib/sidebar/order-tab.svelte"; import Order from "$lib/sidebar/order-tab.svelte";
import type { MobileTool } from "$lib/sidebar/types"; import type { MobileTool } from "$lib/sidebar/types";
import { GameStateStore, GAME_STATE_CONTEXT_KEY } from "$lib/game-state.svelte";
import { session } from "$lib/session-store.svelte";
import { loadStore } from "../../../platform/store/index";
import { loadCore } from "../../../platform/core/index";
import { createEdgeGatewayClient } from "../../../api/connect";
import { GalaxyClient } from "../../../api/galaxy-client";
import { GATEWAY_BASE_URL, GATEWAY_RESPONSE_PUBLIC_KEY } from "$lib/env";
let { children } = $props(); let { children } = $props();
@@ -36,9 +52,54 @@ swap.
isOnMap ? mobileTool : "map", isOnMap ? mobileTool : "map",
); );
const gameState = new GameStateStore();
setContext(GAME_STATE_CONTEXT_KEY, gameState);
function toggleSidebar(): void { function toggleSidebar(): void {
sidebarOpen = !sidebarOpen; sidebarOpen = !sidebarOpen;
} }
async function sha256(payload: Uint8Array): Promise<Uint8Array> {
const digest = await crypto.subtle.digest("SHA-256", payload as BufferSource);
return new Uint8Array(digest);
}
onMount(() => {
(async (): Promise<void> => {
if (
session.keypair === null ||
session.deviceSessionId === null ||
GATEWAY_RESPONSE_PUBLIC_KEY.length === 0
) {
return;
}
const keypair = session.keypair;
const deviceSessionId = session.deviceSessionId;
try {
const [{ cache }, core] = await Promise.all([loadStore(), loadCore()]);
const client = new GalaxyClient({
core,
edge: createEdgeGatewayClient(GATEWAY_BASE_URL),
signer: (canonical) => keypair.sign(canonical),
sha256,
deviceSessionId,
gatewayResponsePublicKey: GATEWAY_RESPONSE_PUBLIC_KEY,
});
await gameState.init({ client, cache, gameId });
} catch (err) {
gameState.failBootstrap(describeBootstrapError(err));
}
})();
});
onDestroy(() => {
gameState.dispose();
});
function describeBootstrapError(err: unknown): string {
if (err instanceof Error) return err.message;
return "request failed";
}
</script> </script>
<div class="game-shell" data-testid="game-shell"> <div class="game-shell" data-testid="game-shell">
@@ -35,6 +35,7 @@ export interface GameFixture {
enrollmentEndsAtMs?: bigint; enrollmentEndsAtMs?: bigint;
createdAtMs?: bigint; createdAtMs?: bigint;
updatedAtMs?: bigint; updatedAtMs?: bigint;
currentTurn?: number;
} }
export interface ApplicationFixture { export interface ApplicationFixture {
@@ -79,6 +80,7 @@ function encodeGame(builder: Builder, game: GameFixture): number {
GameSummary.addEnrollmentEndsAtMs(builder, game.enrollmentEndsAtMs ?? DEFAULT_TIME_MS); GameSummary.addEnrollmentEndsAtMs(builder, game.enrollmentEndsAtMs ?? DEFAULT_TIME_MS);
GameSummary.addCreatedAtMs(builder, game.createdAtMs ?? DEFAULT_TIME_MS); GameSummary.addCreatedAtMs(builder, game.createdAtMs ?? DEFAULT_TIME_MS);
GameSummary.addUpdatedAtMs(builder, game.updatedAtMs ?? DEFAULT_TIME_MS); GameSummary.addUpdatedAtMs(builder, game.updatedAtMs ?? DEFAULT_TIME_MS);
GameSummary.addCurrentTurn(builder, game.currentTurn ?? 0);
return GameSummary.endGameSummary(builder); return GameSummary.endGameSummary(builder);
} }
@@ -0,0 +1,129 @@
// Phase 11 helpers for forging FlatBuffers report payloads in e2e
// tests. Mirrors the engine's `report.Report` shape so the mocked
// gateway can return realistic data without standing up the real
// engine container.
//
// Phase 11 only renders planets, so the helpers keep the report shape
// minimal (turn / dimensions / planet vectors). Later phases extend
// the helper as ships, fleets, sciences, etc. land.
import { Builder } from "flatbuffers";
import {
LocalPlanet,
OtherPlanet,
Report,
UnidentifiedPlanet,
UninhabitedPlanet,
} from "../../../src/proto/galaxy/fbs/report";
export interface PlanetFixture {
number: number;
name: string;
x: number;
y: number;
}
export interface OtherPlanetFixture extends PlanetFixture {
owner: string;
}
export interface ReportFixture {
turn: number;
mapWidth?: number;
mapHeight?: number;
localPlanets?: PlanetFixture[];
otherPlanets?: OtherPlanetFixture[];
uninhabitedPlanets?: PlanetFixture[];
unidentifiedPlanets?: { number: number; x: number; y: number }[];
}
export function buildReportPayload(fixture: ReportFixture): Uint8Array {
const builder = new Builder(512);
const localOffsets = (fixture.localPlanets ?? []).map((planet) => {
const name = builder.createString(planet.name);
LocalPlanet.startLocalPlanet(builder);
LocalPlanet.addNumber(builder, BigInt(planet.number));
LocalPlanet.addX(builder, planet.x);
LocalPlanet.addY(builder, planet.y);
LocalPlanet.addName(builder, name);
LocalPlanet.addSize(builder, 10);
LocalPlanet.addResources(builder, 0.5);
LocalPlanet.addPopulation(builder, 0);
LocalPlanet.addIndustry(builder, 0);
return LocalPlanet.endLocalPlanet(builder);
});
const otherOffsets = (fixture.otherPlanets ?? []).map((planet) => {
const name = builder.createString(planet.name);
const owner = builder.createString(planet.owner);
OtherPlanet.startOtherPlanet(builder);
OtherPlanet.addNumber(builder, BigInt(planet.number));
OtherPlanet.addX(builder, planet.x);
OtherPlanet.addY(builder, planet.y);
OtherPlanet.addName(builder, name);
OtherPlanet.addOwner(builder, owner);
OtherPlanet.addSize(builder, 9);
return OtherPlanet.endOtherPlanet(builder);
});
const uninhabitedOffsets = (fixture.uninhabitedPlanets ?? []).map(
(planet) => {
const name = builder.createString(planet.name);
UninhabitedPlanet.startUninhabitedPlanet(builder);
UninhabitedPlanet.addNumber(builder, BigInt(planet.number));
UninhabitedPlanet.addX(builder, planet.x);
UninhabitedPlanet.addY(builder, planet.y);
UninhabitedPlanet.addName(builder, name);
UninhabitedPlanet.addSize(builder, 6);
return UninhabitedPlanet.endUninhabitedPlanet(builder);
},
);
const unidentifiedOffsets = (fixture.unidentifiedPlanets ?? []).map(
(planet) => {
UnidentifiedPlanet.startUnidentifiedPlanet(builder);
UnidentifiedPlanet.addNumber(builder, BigInt(planet.number));
UnidentifiedPlanet.addX(builder, planet.x);
UnidentifiedPlanet.addY(builder, planet.y);
return UnidentifiedPlanet.endUnidentifiedPlanet(builder);
},
);
const localVec =
localOffsets.length === 0
? null
: Report.createLocalPlanetVector(builder, localOffsets);
const otherVec =
otherOffsets.length === 0
? null
: Report.createOtherPlanetVector(builder, otherOffsets);
const uninhabitedVec =
uninhabitedOffsets.length === 0
? null
: Report.createUninhabitedPlanetVector(builder, uninhabitedOffsets);
const unidentifiedVec =
unidentifiedOffsets.length === 0
? null
: Report.createUnidentifiedPlanetVector(builder, unidentifiedOffsets);
const totalPlanets =
(fixture.localPlanets ?? []).length +
(fixture.otherPlanets ?? []).length +
(fixture.uninhabitedPlanets ?? []).length +
(fixture.unidentifiedPlanets ?? []).length;
Report.startReport(builder);
Report.addTurn(builder, BigInt(fixture.turn));
Report.addWidth(builder, fixture.mapWidth ?? 4000);
Report.addHeight(builder, fixture.mapHeight ?? 4000);
Report.addPlanetCount(builder, totalPlanets);
if (localVec !== null) Report.addLocalPlanet(builder, localVec);
if (otherVec !== null) Report.addOtherPlanet(builder, otherVec);
if (uninhabitedVec !== null) Report.addUninhabitedPlanet(builder, uninhabitedVec);
if (unidentifiedVec !== null) Report.addUnidentifiedPlanet(builder, unidentifiedVec);
const reportOff = Report.endReport(builder);
builder.finish(reportOff);
return builder.asUint8Array();
}
@@ -0,0 +1,239 @@
// Phase 11 end-to-end coverage for the live map integration. Boots
// an authenticated session through `/__debug/store`, mocks the two
// gateway calls the layout makes (`lobby.my.games.list` and
// `user.games.report`), navigates to `/games/<game-id>/map`, and
// asserts the chrome reflects the live data: turn counter shows the
// reported turn, the map view enters its `ready` state with a
// non-zero planet count, and a zero-planet response renders the
// empty world without errors.
import { fromJson, type JsonValue } from "@bufbuild/protobuf";
import { expect, test, type Page } from "@playwright/test";
import { ByteBuffer } from "flatbuffers";
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
import { UUID } from "../../src/proto/galaxy/fbs/common";
import { GameReportRequest } from "../../src/proto/galaxy/fbs/report";
import { forgeExecuteCommandResponseJson } from "./fixtures/sign-response";
import {
buildMyGamesListPayload,
type GameFixture,
} from "./fixtures/lobby-fbs";
import { buildReportPayload } from "./fixtures/report-fbs";
const SESSION_ID = "phase-11-map-session";
const GAME_ID = "11111111-2222-3333-4444-555555555555";
interface MockOpts {
currentTurn: number;
report: Parameters<typeof buildReportPayload>[0];
gameId?: string;
}
interface MockState {
reportRequests: Array<{ gameId: string; turn: number }>;
}
async function mockGateway(page: Page, opts: MockOpts): Promise<MockState> {
const state: MockState = { reportRequests: [] };
const gameId = opts.gameId ?? GAME_ID;
const game: GameFixture = {
gameId,
gameName: "Phase 11 Game",
gameType: "private",
status: "running",
ownerUserId: "user-1",
minPlayers: 2,
maxPlayers: 8,
enrollmentEndsAtMs: BigInt(Date.now() + 86_400_000),
createdAtMs: BigInt(Date.now() - 86_400_000),
updatedAtMs: BigInt(Date.now()),
currentTurn: opts.currentTurn,
};
await page.route(
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
async (route) => {
const reqText = route.request().postData();
if (reqText === null) {
await route.fulfill({ status: 400 });
return;
}
const req = fromJson(
ExecuteCommandRequestSchema,
JSON.parse(reqText) as JsonValue,
);
let resultCode = "ok";
let payload: Uint8Array;
switch (req.messageType) {
case "lobby.my.games.list":
payload = buildMyGamesListPayload([game]);
break;
case "user.games.report": {
const decoded = GameReportRequest.getRootAsGameReportRequest(
new ByteBuffer(req.payloadBytes),
);
const idStruct = decoded.gameId(new UUID());
const hi = idStruct?.hi() ?? 0n;
const lo = idStruct?.lo() ?? 0n;
state.reportRequests.push({
gameId: hiLoToUuid(hi, lo),
turn: decoded.turn(),
});
payload = buildReportPayload(opts.report);
break;
}
default:
resultCode = "internal_error";
payload = new Uint8Array();
}
const body = await forgeExecuteCommandResponseJson({
requestId: req.requestId,
timestampMs: BigInt(Date.now()),
resultCode,
payloadBytes: payload,
});
await route.fulfill({
status: 200,
contentType: "application/json",
body,
});
},
);
// Hold the SubscribeEvents stream open indefinitely. The
// revocation watcher in `lib/revocation-watcher.ts` treats a clean
// end-of-stream as `session_invalidation` and calls
// `session.signOut("revoked")`, which would bounce the page back
// to `/login`. Playwright aborts pending routes on test teardown,
// the watcher's catch path logs the abort and returns without a
// sign-out — same convention as `tests/e2e/lobby-flow.spec.ts`.
await page.route(
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
async () => {
await new Promise<void>(() => {});
},
);
return state;
}
function hiLoToUuid(hi: bigint, lo: bigint): string {
const toHex = (v: bigint): string => v.toString(16).padStart(16, "0");
const full = toHex(hi) + toHex(lo);
return [
full.slice(0, 8),
full.slice(8, 12),
full.slice(12, 16),
full.slice(16, 20),
full.slice(20, 32),
].join("-");
}
async function bootSession(page: Page): Promise<void> {
await page.goto("/__debug/store");
await expect(page.getByTestId("debug-store-ready")).toBeVisible();
await page.waitForFunction(() => window.__galaxyDebug?.ready === true);
await page.evaluate(() => window.__galaxyDebug!.clearSession());
await page.evaluate(
(id) => window.__galaxyDebug!.setDeviceSessionId(id),
SESSION_ID,
);
}
test("map view renders the reported turn and planet count from a live report", async ({
page,
}) => {
const mocks = await mockGateway(page, {
currentTurn: 4,
report: {
turn: 4,
mapWidth: 4000,
mapHeight: 4000,
localPlanets: [
{ number: 1, name: "Home", x: 1000, y: 1000 },
{ number: 2, name: "Outpost", x: 1500, y: 1300 },
],
otherPlanets: [
{
number: 3,
name: "Frontier",
x: 2200,
y: 2200,
owner: "Federation",
},
],
uninhabitedPlanets: [{ number: 4, name: "Rock", x: 800, y: 2400 }],
},
});
await bootSession(page);
await page.goto(`/games/${GAME_ID}/map`);
await expect(page.getByTestId("active-view-map")).toHaveAttribute(
"data-status",
"ready",
);
await expect(page.getByTestId("turn-counter")).toContainText("turn 4");
await expect(page.getByTestId("map-canvas-wrap")).toHaveAttribute(
"data-planet-count",
"4",
);
expect(mocks.reportRequests.length).toBeGreaterThanOrEqual(1);
expect(mocks.reportRequests[0]?.gameId).toBe(GAME_ID);
expect(mocks.reportRequests[0]?.turn).toBe(4);
});
test("zero-planet game renders the empty world without errors", async ({
page,
}) => {
await mockGateway(page, {
currentTurn: 0,
report: {
turn: 0,
mapWidth: 4000,
mapHeight: 4000,
},
});
await bootSession(page);
await page.goto(`/games/${GAME_ID}/map`);
await expect(page.getByTestId("active-view-map")).toHaveAttribute(
"data-status",
"ready",
);
await expect(page.getByTestId("turn-counter")).toContainText("turn 0");
await expect(page.getByTestId("map-canvas-wrap")).toHaveAttribute(
"data-planet-count",
"0",
);
await expect(page.getByTestId("map-error")).not.toBeVisible();
await expect(page.getByTestId("map-mount-error")).not.toBeVisible();
});
test("missing-membership game surfaces an error instead of a blank canvas", async ({
page,
}) => {
// The gateway returns lobby.my.games.list with a different game id
// so the layout's gameState lookup misses; the store flips to
// `error` and the map view renders the localised error overlay.
await mockGateway(page, {
currentTurn: 0,
gameId: "99999999-aaaa-bbbb-cccc-000000000000",
report: { turn: 0 },
});
await bootSession(page);
await page.goto(`/games/${GAME_ID}/map`);
await expect(page.getByTestId("map-error")).toBeVisible();
await expect(page.getByTestId("active-view-map")).toHaveAttribute(
"data-status",
"error",
);
});
+10 -3
View File
@@ -24,11 +24,18 @@ beforeEach(() => {
}); });
describe("active-view stubs", () => { describe("active-view stubs", () => {
test("map stub renders title and coming-soon copy", () => { test("map view renders loading overlay when no game-state context is provided", () => {
// The live integration in `lib/active-view/map.svelte` (Phase 11)
// reads its data from a `GameStateStore` provided through context
// by `routes/games/[id]/+layout.svelte`. Without the context the
// store reference is `undefined` and the view stays in the
// `idle` branch, surfacing the localised loading overlay so the
// shell never renders an empty active-view slot.
const ui = render(MapView); const ui = render(MapView);
const node = ui.getByTestId("active-view-map"); const node = ui.getByTestId("active-view-map");
expect(node).toHaveTextContent("map"); expect(node).toHaveAttribute("data-status", "idle");
expect(node).toHaveTextContent("coming soon"); expect(ui.getByTestId("map-loading")).toBeInTheDocument();
expect(ui.getByTestId("map-canvas-wrap")).toBeInTheDocument();
}); });
test("table stub maps a kebab-case entity to the right i18n title", () => { test("table stub maps a kebab-case entity to the right i18n title", () => {
+264
View File
@@ -0,0 +1,264 @@
// Vitest coverage for the per-game runes store
// (`lib/game-state.svelte.ts`). The test stubs `lobby.my.games.list`
// and `user.games.report` at module level and drives the store
// through its lifecycle: init → ready → error → setTurn → wrap-mode
// persistence.
import "@testing-library/jest-dom/vitest";
import "fake-indexeddb/auto";
import {
afterEach,
beforeEach,
describe,
expect,
test,
vi,
} from "vitest";
import { Builder } from "flatbuffers";
import { GameStateStore } from "../src/lib/game-state.svelte";
import type { GalaxyClient } from "../src/api/galaxy-client";
import type { Cache } from "../src/platform/store/index";
import { IDBCache } from "../src/platform/store/idb-cache";
import { openGalaxyDB, type GalaxyDB } from "../src/platform/store/idb";
import type { IDBPDatabase } from "idb";
import { UUID } from "../src/proto/galaxy/fbs/common";
import {
LocalPlanet,
Report,
} from "../src/proto/galaxy/fbs/report";
const listMyGamesSpy = vi.fn();
vi.mock("../src/api/lobby", async () => {
const actual = await vi.importActual<typeof import("../src/api/lobby")>(
"../src/api/lobby",
);
return {
...actual,
listMyGames: (...args: unknown[]) => listMyGamesSpy(...args),
};
});
let db: IDBPDatabase<GalaxyDB>;
let dbName: string;
let cache: Cache;
beforeEach(async () => {
dbName = `galaxy-game-state-test-${crypto.randomUUID()}`;
db = await openGalaxyDB(dbName);
cache = new IDBCache(db);
listMyGamesSpy.mockReset();
});
afterEach(async () => {
db.close();
await new Promise<void>((resolve) => {
const req = indexedDB.deleteDatabase(dbName);
req.onsuccess = () => resolve();
req.onerror = () => resolve();
req.onblocked = () => resolve();
});
});
const GAME_ID = "11111111-2222-3333-4444-555555555555";
function makeGameSummary(currentTurn: number): {
gameId: string;
gameName: string;
gameType: string;
status: string;
ownerUserId: string;
minPlayers: number;
maxPlayers: number;
enrollmentEndsAt: Date;
createdAt: Date;
updatedAt: Date;
currentTurn: number;
} {
return {
gameId: GAME_ID,
gameName: "Test Game",
gameType: "private",
status: "running",
ownerUserId: "owner-1",
minPlayers: 2,
maxPlayers: 8,
enrollmentEndsAt: new Date(),
createdAt: new Date(),
updatedAt: new Date(),
currentTurn,
};
}
interface PlanetFixture {
number: number;
name: string;
x: number;
y: number;
}
function buildReportPayload(opts: {
turn: number;
width?: number;
height?: number;
planets?: PlanetFixture[];
}): Uint8Array {
const builder = new Builder(256);
const planetOffsets = (opts.planets ?? []).map((planet) => {
const name = builder.createString(planet.name);
LocalPlanet.startLocalPlanet(builder);
LocalPlanet.addNumber(builder, BigInt(planet.number));
LocalPlanet.addX(builder, planet.x);
LocalPlanet.addY(builder, planet.y);
LocalPlanet.addName(builder, name);
LocalPlanet.addSize(builder, 10);
LocalPlanet.addResources(builder, 0.5);
return LocalPlanet.endLocalPlanet(builder);
});
const localPlanetVec =
planetOffsets.length === 0
? null
: Report.createLocalPlanetVector(builder, planetOffsets);
Report.startReport(builder);
Report.addTurn(builder, BigInt(opts.turn));
Report.addWidth(builder, opts.width ?? 4000);
Report.addHeight(builder, opts.height ?? 4000);
Report.addPlanetCount(builder, planetOffsets.length);
if (localPlanetVec !== null) {
Report.addLocalPlanet(builder, localPlanetVec);
}
const reportOff = Report.endReport(builder);
builder.finish(reportOff);
return builder.asUint8Array();
}
function makeFakeClient(
executeCommand: (
messageType: string,
payload: Uint8Array,
) => Promise<{ resultCode: string; payloadBytes: Uint8Array }>,
): GalaxyClient {
return { executeCommand } as unknown as GalaxyClient;
}
describe("GameStateStore", () => {
test("init transitions through loading and ready when both calls succeed", async () => {
listMyGamesSpy.mockResolvedValue([makeGameSummary(7)]);
const calls: Array<{ messageType: string; payload: Uint8Array }> = [];
const client = makeFakeClient(async (messageType, payload) => {
calls.push({ messageType, payload });
return {
resultCode: "ok",
payloadBytes: buildReportPayload({
turn: 7,
planets: [{ number: 1, name: "Home", x: 100, y: 100 }],
}),
};
});
const store = new GameStateStore();
expect(store.status).toBe("idle");
await store.init({ client, cache, gameId: GAME_ID });
expect(listMyGamesSpy).toHaveBeenCalledTimes(1);
expect(calls.length).toBe(1);
expect(calls[0]?.messageType).toBe("user.games.report");
expect(store.status).toBe("ready");
expect(store.report).not.toBeNull();
expect(store.report?.turn).toBe(7);
expect(store.report?.planets.length).toBe(1);
expect(store.report?.planets[0]?.kind).toBe("local");
store.dispose();
});
test("init surfaces an error when the game is missing from lobby", async () => {
listMyGamesSpy.mockResolvedValue([makeGameSummary(0).gameId === "other" ? null : makeGameSummary(0)].filter(Boolean));
// Replace the helper above's awkward filter with an explicit
// mismatched id so the lookup miss is unambiguous.
listMyGamesSpy.mockResolvedValue([
{ ...makeGameSummary(2), gameId: "different-game-id" },
]);
const client = makeFakeClient(async () => ({
resultCode: "ok",
payloadBytes: buildReportPayload({ turn: 0 }),
}));
const store = new GameStateStore();
await store.init({ client, cache, gameId: GAME_ID });
expect(store.status).toBe("error");
expect(store.error).toMatch(/not in your list/);
expect(store.report).toBeNull();
store.dispose();
});
test("init surfaces error when user.games.report returns a non-ok result", async () => {
listMyGamesSpy.mockResolvedValue([makeGameSummary(0)]);
const client = makeFakeClient(async () => ({
resultCode: "forbidden",
payloadBytes: new TextEncoder().encode(
JSON.stringify({ code: "forbidden", message: "no membership" }),
),
}));
const store = new GameStateStore();
await store.init({ client, cache, gameId: GAME_ID });
expect(store.status).toBe("error");
expect(store.error).toMatch(/no membership/);
store.dispose();
});
test("setTurn loads a different turn snapshot", async () => {
listMyGamesSpy.mockResolvedValue([makeGameSummary(3)]);
const turns: number[] = [];
const client = makeFakeClient(async () => {
const turn = turns.length === 0 ? 3 : 1;
turns.push(turn);
return {
resultCode: "ok",
payloadBytes: buildReportPayload({ turn }),
};
});
const store = new GameStateStore();
await store.init({ client, cache, gameId: GAME_ID });
expect(store.report?.turn).toBe(3);
await store.setTurn(1);
expect(store.status).toBe("ready");
expect(store.report?.turn).toBe(1);
store.dispose();
});
test("setWrapMode persists across instances through Cache", async () => {
listMyGamesSpy.mockResolvedValue([makeGameSummary(0)]);
const client = makeFakeClient(async () => ({
resultCode: "ok",
payloadBytes: buildReportPayload({ turn: 0 }),
}));
const a = new GameStateStore();
await a.init({ client, cache, gameId: GAME_ID });
expect(a.wrapMode).toBe("torus");
await a.setWrapMode("no-wrap");
expect(a.wrapMode).toBe("no-wrap");
a.dispose();
const b = new GameStateStore();
await b.init({ client, cache, gameId: GAME_ID });
expect(b.wrapMode).toBe("no-wrap");
b.dispose();
});
test("failBootstrap moves the store into the error state with the given message", () => {
const store = new GameStateStore();
store.failBootstrap("device session missing");
expect(store.status).toBe("error");
expect(store.error).toBe("device session missing");
});
});
+4 -2
View File
@@ -70,7 +70,7 @@ function makeStub(
return { client: stub, captured }; return { client: stub, captured };
} }
function encodeGameSummary(builder: Builder): number { function encodeGameSummary(builder: Builder, currentTurn: number = 0): number {
const gameId = builder.createString("g-1"); const gameId = builder.createString("g-1");
const gameName = builder.createString("Test Game"); const gameName = builder.createString("Test Game");
const gameType = builder.createString("private"); const gameType = builder.createString("private");
@@ -87,6 +87,7 @@ function encodeGameSummary(builder: Builder): number {
GameSummary.addEnrollmentEndsAtMs(builder, 1_780_000_000_000n); GameSummary.addEnrollmentEndsAtMs(builder, 1_780_000_000_000n);
GameSummary.addCreatedAtMs(builder, 1_770_000_000_000n); GameSummary.addCreatedAtMs(builder, 1_770_000_000_000n);
GameSummary.addUpdatedAtMs(builder, 1_770_000_000_000n); GameSummary.addUpdatedAtMs(builder, 1_770_000_000_000n);
GameSummary.addCurrentTurn(builder, currentTurn);
return GameSummary.endGameSummary(builder); return GameSummary.endGameSummary(builder);
} }
@@ -133,7 +134,7 @@ describe("lobby.ts wrappers", () => {
test("listMyGames decodes the response and reports the message type", async () => { test("listMyGames decodes the response and reports the message type", async () => {
const { client, captured } = makeStub(() => { const { client, captured } = makeStub(() => {
const builder = new Builder(256); const builder = new Builder(256);
const item = encodeGameSummary(builder); const item = encodeGameSummary(builder, 5);
const items = MyGamesListResponse.createItemsVector(builder, [item]); const items = MyGamesListResponse.createItemsVector(builder, [item]);
MyGamesListResponse.startMyGamesListResponse(builder); MyGamesListResponse.startMyGamesListResponse(builder);
MyGamesListResponse.addItems(builder, items); MyGamesListResponse.addItems(builder, items);
@@ -146,6 +147,7 @@ describe("lobby.ts wrappers", () => {
expect(games.length).toBe(1); expect(games.length).toBe(1);
expect(games[0]!.gameId).toBe("g-1"); expect(games[0]!.gameId).toBe("g-1");
expect(games[0]!.minPlayers).toBe(2); expect(games[0]!.minPlayers).toBe(2);
expect(games[0]!.currentTurn).toBe(5);
}); });
test("listPublicGames passes pagination and decodes pageSize/total", async () => { test("listPublicGames passes pagination and decodes pageSize/total", async () => {
+6 -1
View File
@@ -43,19 +43,21 @@ interface GameSummaryFixture {
enrollmentEndsAtMs: bigint; enrollmentEndsAtMs: bigint;
createdAtMs: bigint; createdAtMs: bigint;
updatedAtMs: bigint; updatedAtMs: bigint;
currentTurn: number;
} }
const PRIVATE_GAME: GameSummaryFixture = { const PRIVATE_GAME: GameSummaryFixture = {
gameId: "game-private-7c8f", gameId: "game-private-7c8f",
gameName: "First Contact", gameName: "First Contact",
gameType: "private", gameType: "private",
status: "draft", status: "running",
ownerUserId: "user-9912", ownerUserId: "user-9912",
minPlayers: 2, minPlayers: 2,
maxPlayers: 8, maxPlayers: 8,
enrollmentEndsAtMs: 1_780_000_000_000n, enrollmentEndsAtMs: 1_780_000_000_000n,
createdAtMs: 1_770_000_000_000n, createdAtMs: 1_770_000_000_000n,
updatedAtMs: 1_770_000_300_000n, updatedAtMs: 1_770_000_300_000n,
currentTurn: 7,
}; };
const PUBLIC_GAME: GameSummaryFixture = { const PUBLIC_GAME: GameSummaryFixture = {
@@ -69,6 +71,7 @@ const PUBLIC_GAME: GameSummaryFixture = {
enrollmentEndsAtMs: 1_780_500_000_000n, enrollmentEndsAtMs: 1_780_500_000_000n,
createdAtMs: 1_770_500_000_000n, createdAtMs: 1_770_500_000_000n,
updatedAtMs: 1_770_600_000_000n, updatedAtMs: 1_770_600_000_000n,
currentTurn: 0,
}; };
function encodeGameSummary(builder: Builder, value: GameSummaryFixture): number { function encodeGameSummary(builder: Builder, value: GameSummaryFixture): number {
@@ -88,6 +91,7 @@ function encodeGameSummary(builder: Builder, value: GameSummaryFixture): number
GameSummary.addEnrollmentEndsAtMs(builder, value.enrollmentEndsAtMs); GameSummary.addEnrollmentEndsAtMs(builder, value.enrollmentEndsAtMs);
GameSummary.addCreatedAtMs(builder, value.createdAtMs); GameSummary.addCreatedAtMs(builder, value.createdAtMs);
GameSummary.addUpdatedAtMs(builder, value.updatedAtMs); GameSummary.addUpdatedAtMs(builder, value.updatedAtMs);
GameSummary.addCurrentTurn(builder, value.currentTurn);
return GameSummary.endGameSummary(builder); return GameSummary.endGameSummary(builder);
} }
@@ -104,6 +108,7 @@ function expectGameSummary(actual: GameSummary | null, want: GameSummaryFixture)
expect(got.enrollmentEndsAtMs()).toBe(want.enrollmentEndsAtMs); expect(got.enrollmentEndsAtMs()).toBe(want.enrollmentEndsAtMs);
expect(got.createdAtMs()).toBe(want.createdAtMs); expect(got.createdAtMs()).toBe(want.createdAtMs);
expect(got.updatedAtMs()).toBe(want.updatedAtMs); expect(got.updatedAtMs()).toBe(want.updatedAtMs);
expect(got.currentTurn()).toBe(want.currentTurn);
} }
describe("lobby FlatBuffers TS bindings", () => { describe("lobby FlatBuffers TS bindings", () => {
+2
View File
@@ -136,6 +136,7 @@ function makeGame(id: string, name: string, status = "draft") {
enrollmentEndsAt: baseDate, enrollmentEndsAt: baseDate,
createdAt: baseDate, createdAt: baseDate,
updatedAt: baseDate, updatedAt: baseDate,
currentTurn: 0,
}; };
} }
@@ -151,6 +152,7 @@ function makePublicGame(id: string, name: string) {
enrollmentEndsAt: baseDate, enrollmentEndsAt: baseDate,
createdAt: baseDate, createdAt: baseDate,
updatedAt: baseDate, updatedAt: baseDate,
currentTurn: 0,
}; };
} }
+114
View File
@@ -0,0 +1,114 @@
// Vitest unit coverage for `map/state-binding.ts`. The function
// translates a Phase 11 `GameReport` into a renderer-ready `World`
// containing one Point primitive per planet across all four kinds
// (local / other / uninhabited / unidentified). The tests assert
// the world dimensions match the report, the planet ids are the
// engine numbers, the kind-specific styles differ, and a zero-planet
// report still produces a well-formed empty World.
import "@testing-library/jest-dom/vitest";
import { describe, expect, test } from "vitest";
import type { GameReport } from "../src/api/game-state";
import { reportToWorld } from "../src/map/state-binding";
function makeReport(overrides: Partial<GameReport> = {}): GameReport {
return {
turn: 1,
mapWidth: 4000,
mapHeight: 4000,
planetCount: 0,
planets: [],
...overrides,
};
}
describe("reportToWorld", () => {
test("uses report dimensions for the World", () => {
const world = reportToWorld(makeReport({ mapWidth: 3200, mapHeight: 1600 }));
expect(world.width).toBe(3200);
expect(world.height).toBe(1600);
});
test("emits one Point primitive per planet across all four kinds", () => {
const world = reportToWorld(
makeReport({
planets: [
{ number: 1, name: "Home", x: 100, y: 100, kind: "local", owner: null, size: 12, resources: 0.5 },
{ number: 2, name: "Alpha", x: 200, y: 100, kind: "other", owner: "Federation", size: 8, resources: 0.3 },
{ number: 3, name: "Rock", x: 100, y: 200, kind: "uninhabited", owner: null, size: 4, resources: 0.1 },
{ number: 4, name: "", x: 200, y: 200, kind: "unidentified", owner: null, size: null, resources: null },
],
}),
);
expect(world.primitives.length).toBe(4);
for (const p of world.primitives) {
expect(p.kind).toBe("point");
}
});
test("propagates planet number as primitive id and coordinates verbatim", () => {
const world = reportToWorld(
makeReport({
planets: [
{ number: 42, name: "Home", x: 123.5, y: 456.25, kind: "local", owner: null, size: 10, resources: 0.5 },
],
}),
);
const [planet] = world.primitives;
expect(planet?.id).toBe(42);
expect(planet?.kind).toBe("point");
if (planet?.kind === "point") {
expect(planet.x).toBe(123.5);
expect(planet.y).toBe(456.25);
}
});
test("uses distinct styles for each planet kind", () => {
const world = reportToWorld(
makeReport({
planets: [
{ number: 1, name: "L", x: 0, y: 0, kind: "local", owner: null, size: 1, resources: 0 },
{ number: 2, name: "O", x: 1, y: 0, kind: "other", owner: "Foe", size: 1, resources: 0 },
{ number: 3, name: "U", x: 2, y: 0, kind: "uninhabited", owner: null, size: 1, resources: 0 },
{ number: 4, name: "?", x: 3, y: 0, kind: "unidentified", owner: null, size: null, resources: null },
],
}),
);
const fills = world.primitives.map((p) => p.style.fillColor);
const unique = new Set(fills);
expect(unique.size).toBe(fills.length);
});
test("zero-planet report yields an empty primitive list and well-formed World", () => {
const world = reportToWorld(makeReport({ planets: [] }));
expect(world.primitives.length).toBe(0);
expect(world.width).toBeGreaterThan(0);
expect(world.height).toBeGreaterThan(0);
});
test("guards against zero / negative dimensions in the report", () => {
const world = reportToWorld(
makeReport({ mapWidth: 0, mapHeight: -1, planets: [] }),
);
// World's constructor rejects non-positive dimensions; the
// binding falls back to 1×1 so a malformed report cannot crash
// the renderer.
expect(world.width).toBeGreaterThan(0);
expect(world.height).toBeGreaterThan(0);
});
test("local planets carry higher priority than unidentified", () => {
const world = reportToWorld(
makeReport({
planets: [
{ number: 1, name: "Home", x: 0, y: 0, kind: "local", owner: null, size: 1, resources: 0 },
{ number: 2, name: "?", x: 0, y: 0, kind: "unidentified", owner: null, size: null, resources: null },
],
}),
);
const local = world.primitives.find((p) => p.id === 1);
const unknown = world.primitives.find((p) => p.id === 2);
expect(local?.priority ?? 0).toBeGreaterThan(unknown?.priority ?? 0);
});
});