Multi-System Data Model

Issue: #728

Overview

Galaxy currently assumes a single star system (Sol). This spec adds system_id plumbing to the data model — proto definitions, Redis state, PostgreSQL schema, and galaxy service RPCs — so all other multi-system work (#729–#736) can build on it.

Scope

In scope:

  • system_id field on all entity types (bodies, ships, stations, jump gates)
  • Per-system index sets in Redis
  • StarSystem data structure and configuration
  • ListSystems / GetSystemBodies RPCs
  • PostgreSQL migration for ships.system_id
  • Backward compatibility (lazy defaults to “sol”)

Out of scope:

  • Per-system physics simulation
  • Per-system WebSocket filtering
  • Jump gate transit logic
  • New star system ephemeris data
  • Web-client changes
  • Tick-engine behavior changes

StarSystem Data Structure

{
  "id": "sol",
  "name": "Sol",
  "star_body": "Sun",
  "position": [0, 0, 0]
}
Field Type Description
id string Unique system identifier (lowercase, e.g. “sol”)
name string Display name (e.g. “Sol”)
star_body string Name of the primary star body
position [x,y,z] Galactic position in meters (for inter-system navigation)

Systems are defined in services/shared/galaxy_config/shared_data.json under the SYSTEMS key.

Redis Namespacing

Decision: Flat keys with system_id field + index sets.

Entity keys remain flat (body:{name}, ship:{uuid}, station:{uuid}, jumpgate:{uuid}). Each entity hash gains a system_id field. Per-system index sets enable efficient lookups.

Index Sets

Key Pattern Type Description
system:{system_id}:bodies Set Body names in this system
system:{system_id}:ships Set Ship UUIDs in this system
system:{system_id}:stations Set Station UUIDs in this system
system:{system_id}:jumpgates Set Jump gate UUIDs in this system

Index sets are maintained by the physics service on entity create/delete/clear, and rebuilt by tick-engine on snapshot restore.

Backward Compatibility

Missing system_id field on any entity hash defaults to "sol". All reads use .get("system_id", "sol") or equivalent.

Proto Changes

galaxy.proto

Message Field Number
CelestialBody string system_id 12
GetBodiesRequest string system_id 1 (optional filter)

New messages:

  • StarSystem { string id = 1; string name = 2; Vec3 position = 3; string star_body = 4; }
  • ListSystemsRequest {} / ListSystemsResponse { repeated StarSystem systems = 1; }
  • GetSystemBodiesRequest { string system_id = 1; } — reuses GetBodiesResponse

New RPCs:

  • ListSystems(ListSystemsRequest) returns (ListSystemsResponse)
  • GetSystemBodies(GetSystemBodiesRequest) returns (GetBodiesResponse)

physics.proto

Message Field Number
BodyInit string system_id 11
BodyState string system_id 11
ShipState string system_id 22
StationState string system_id 9
JumpGateState string system_id 9
SpawnShipRequest string system_id 7
SpawnStationRequest string system_id 6
SpawnJumpGateRequest string system_id 5

players.proto

Message Field Number
SpawnNewShipRequest string system_id 5
ShipInfo string system_id 6

PostgreSQL Migration

ALTER TABLE ships ADD COLUMN IF NOT EXISTS system_id VARCHAR(32) NOT NULL DEFAULT 'sol';
CREATE INDEX IF NOT EXISTS idx_ships_system_id ON ships (system_id);

Migration file: db/alembic/versions/009_multi_system.py

Shared Configuration

services/shared/galaxy_config/shared_data.json gains a SYSTEMS key:

"SYSTEMS": {
  "sol": { "name": "Sol", "star_body": "Sun", "position": [0, 0, 0] }
}

Exported as SYSTEMS dict from galaxy_config.__init__.

Service Changes

Galaxy Service

  • CelestialBody model gains system_id: str = "sol"
  • Bodies initialized from ephemeris are assigned system_id="sol"
  • New ListSystems RPC returns systems from shared config
  • New GetSystemBodies RPC filters bodies by system_id
  • GetBodies supports optional system_id filter

Physics Service

  • All entity models (CelestialBody, Ship, Station, JumpGate) gain system_id: str = "sol"
  • Redis state: system_id written to all entity hashes, read with .get("system_id", "sol")
  • Index sets (system:{id}:bodies/ships/stations/jumpgates) maintained on set/delete/clear
  • gRPC: system_id read from proto requests, included in responses

Players Service

  • Ship model gains system_id: str = "sol"
  • Database: system_id included in ship INSERT/SELECT, default “sol” for old rows
  • gRPC: system_id in ShipInfo response, read from SpawnNewShipRequest

Tick-Engine

  • Passes system_id through BodyInit, SpawnStationRequest, SpawnJumpGateRequest
  • Rebuilds system index sets on snapshot restore
  • Cleans up system index sets on reset

API Gateway

  • Includes system_id in WebSocket broadcast dicts (forward compatibility)

Back to top

Galaxy — Kubernetes-based multiplayer space game

This site uses Just the Docs, a documentation theme for Jekyll.