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:stationswith eventsstation.spawnedandstation.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):
- Get primary and secondary body positions/velocities from Redis
- Compute separation vector
r = secondary.pos - primary.pos - Compute orbit normal
n = normalize(r × v_rel) - Rodrigues’ rotation of
rby+60°(L4) or−60°(L5) aroundn - Station position =
primary.pos + r_rotated - 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.