Space Stations

Overview

Space stations are passive orbital objects that orbit under gravity like ships but have no engines, no owner, and hold a fixed attitude. They serve as visual landmarks and spawn reference points for new players.

Station Properties

Property Description
station_id Unique identifier (UUID)
name Human-readable name (e.g., “Gateway Station”)
position 3D position in barycenter coordinates (meters)
velocity 3D velocity in barycenter coordinates (m/s)
attitude Fixed quaternion (identity, non-rotating)
mass Station mass in kg (default: 420,000 kg, ISS-scale)
radius Proximity/collision envelope in meters (default: 50 m)
parent_body Reference body name (e.g., “Earth”)

Physics Behavior

  • Gravity-only integration: Stations use the same Leapfrog integrator as ships but only compute gravitational acceleration (no thrust, no attitude control, no fuel consumption).
  • Symplectic consistency: Both gravity evaluations (at the old and new station positions) must use the same body positions (pre-update snapshot). Using post-update body positions for the second kick breaks symplecticity, causing systematic energy drift. This is especially severe for stations orbiting fast-moving secondaries (e.g., Luna orbiting Earth at 1 km/s), where the parent body displaces significantly between the two gravity evaluations within each timestep.
  • Integration test coverage: The test suite must cover all parent-body motion regimes to prevent symplecticity regressions:
    • Stationary parent: Station orbiting a body at the coordinate origin (baseline Kepler case).
    • Moving parent (secondary): Station orbiting a moon that itself orbits a planet (e.g., Lunar Gateway around Luna). The parent displaces ~1 km/tick; asymmetric kicks cause >20% energy loss per ~6 orbits.
    • Moving parent (primary): Station orbiting a planet that wobbles due to moon interaction (e.g., Gateway Station around Earth). Earth displaces ~12 m/tick from Luna’s pull; the effect is slower but accumulates over hundreds of orbits.
    • Lagrange point (3-body): Station at L4/L5 under combined gravity of primary + secondary. Must remain bound to the Lagrange region over many orbital periods.
    • Asymmetric-kick detection: An explicit regression test must verify that using inconsistent body snapshots (pre-update for first kick, post-update for second) produces measurably worse energy conservation than consistent snapshots.
  • Fixed attitude: The attitude quaternion never changes. Stations do not rotate or tumble.
  • No thrust: Stations have no engines, no fuel, and no reaction control.
  • No player ownership: Stations are admin-managed objects, not tied to any player.

First Station: Gateway Station

Parameter Value
Name Gateway Station
Parent body Earth
Altitude 5,500 km (orbital radius = Earth radius + 5,500 km = 11,871 km from center)
Inclination 0° (equatorial, no spin-axis tilt applied)
Orbital velocity sqrt(GM/r) ~ 5.79 km/s
Mass 420,000 kg
Radius 50 m (proximity envelope)
Attitude Identity quaternion (1, 0, 0, 0)
Visual scale ~100 m span (torus + solar panels)

The equatorial orbit means the station orbits in the ecliptic plane (no tilt rotation applied), placing it in Earth’s MEO region.

Data Storage

  • Redis key pattern: station:{station_id}
  • Redis hash fields mirror the Station dataclass
  • Event stream: galaxy:stations with events station.spawned and station.removed

Admin API

Method Endpoint Description
POST /api/admin/station Spawn a new station
DELETE /api/admin/station/{station_id} Remove a station
GET /api/admin/stations List all stations

POST /api/admin/station

Request body (equatorial orbit):

{
  "name": "Gateway Station",
  "parent_body": "Earth",
  "altitude": 5500000
}

Request body (Lagrange point):

{
  "name": "Frontier Outpost",
  "parent_body": "Earth",
  "secondary_body": "Luna",
  "lagrange_point": 5
}

Response:

{
  "success": true,
  "station_id": "uuid",
  "station": { ... }
}

DELETE /api/admin/station/{station_id}

Response: 204 No Content

GET /api/admin/stations

Response:

{
  "stations": [
    {
      "station_id": "uuid",
      "name": "Gateway Station",
      "position": {"x": ..., "y": ..., "z": ...},
      "velocity": {"x": ..., "y": ..., "z": ...},
      "attitude": {"w": 1, "x": 0, "y": 0, "z": 0},
      "mass": 420000,
      "radius": 50,
      "parent_body": "Earth"
    }
  ]
}

Lunar Gateway

Parameter Value
Name Lunar Gateway
Parent body Luna
Altitude 1,000 km (orbital radius = Luna radius + 1,000 km = 2,737 km from center)
Inclination 0° (equatorial)
Orbital velocity sqrt(GM/r) ~ 0.86 km/s
Mass 420,000 kg
Radius 50 m
Attitude Identity quaternion (1, 0, 0, 0)

The first station orbiting a body other than Earth. Orbits in a low-medium lunar orbit, providing a destination for cross-body rendezvous from Earth orbit.

L5 Station: Frontier Outpost

Parameter Value
Name Frontier Outpost
Primary body Earth
Secondary body Luna
Lagrange point L5 (trailing, −60° from Luna)
Mass 420,000 kg
Radius 50 m
Attitude Identity quaternion (1, 0, 0, 0)

The station is placed at the Earth-Luna L5 point, 60° behind Luna in its orbit. L5 is a stable equilibrium point under the restricted three-body problem — the station naturally remains near L5 under N-body gravity without station-keeping.

Lagrange Point Spawn API

The SpawnStationRequest supports an optional Lagrange point spawn mode. When lagrange_point is set (1–5), the station is placed at the specified Lagrange point of the (parent_body, secondary_body) pair instead of using the altitude field.

Position computation (L4/L5):

  1. Get primary and secondary body positions/velocities from Redis
  2. Compute separation vector r = secondary.pos - primary.pos
  3. Compute orbit normal n = normalize(r × v_rel)
  4. Rodrigues’ rotation of r by +60° (L4) or −60° (L5) around n
  5. Station position = primary.pos + r_rotated
  6. Station velocity = primary.vel + Rodrigues(v_rel, n, same angle)

Player Spawn Near Station

When a player spawns (ship spawn), if a station exists orbiting the same parent body:

  • Position the ship ~1 km offset from the station
  • Match the station’s orbital velocity
  • This gives new players a visual landmark immediately

The SpawnShipRequest proto gains an optional spawn_near_station field to explicitly request spawning near a specific station.

Proximity Warning

When a player’s ship is within 50 km of a station, the client displays a HUD warning with the station name and distance. This threshold is configurable via station_proximity_warning in physics config (default: 50,000 m).

Client Rendering

Cockpit View

  • Station mesh: Modular outpost design (~120 m span) with central spine, docking ports, fuel tanks, radiators, antenna array, rotating hab section, and solar panels. See Outpost Model for full geometry spec.
  • Hab rotation: Hab section rotates at ~1 RPM (0.105 rad/s) around local Y-axis in render loop.
  • Station indicator: CSS2DObject with cyan dot + name + distance (like body indicators but with distinct color).
  • Extrapolation: Linear position extrapolation between ticks (same as ships).
  • Proximity warning: HUD text when distance < 50 km.

Map View

  • Station indicators appear as cyan diamonds (distinct from ship triangles and body dots).
  • Stations appear in the system browser tree under their parent body.

WebSocket State Message

The state message broadcast on each tick includes a stations array alongside bodies and ships:

{
  "type": "state",
  "tick": 12345,
  "bodies": [...],
  "ship": {...},
  "ships": [...],
  "stations": [
    {
      "station_id": "uuid",
      "name": "Gateway Station",
      "position": {"x": ..., "y": ..., "z": ...},
      "velocity": {"x": ..., "y": ..., "z": ...},
      "attitude": {"w": 1, "x": 0, "y": 0, "z": 0},
      "mass": 420000,
      "radius": 50,
      "parent_body": "Earth"
    }
  ]
}

Game Initialization

On fresh game init (after bodies are initialized), the tick engine auto-spawns the default stations (Gateway Station, Lunar Gateway, and Frontier Outpost) via the physics service SpawnStation RPC.

On game reset, all stations are cleared and the default station is re-spawned.

gRPC RPCs

Added to the Physics service:

RPC Request Response Description
SpawnStation SpawnStationRequest SpawnStationResponse Create a station in orbit
RemoveStation RemoveStationRequest RemoveStationResponse Remove a station
GetAllStations GetAllStationsRequest GetAllStationsResponse List all stations
ClearAllStations ClearAllStationsRequest ClearAllStationsResponse Remove all stations

See specs/api/proto/physics.proto for message definitions.


Back to top

Galaxy — Kubernetes-based multiplayer space game

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