Bang integration¶
Status: 🚧 Partial — Path A (admin job) and the core translator are fully wired; Path B bootstrap script is absent, version versioning logic is simplified, Phase 13 invariants are not enforced … · ⚠︎ contains code↔spec divergence (impl audit 2026-06-16)
Status. The
sw2102-bangsidecar is the live generator path: the admin-ui's "Bang a New Galaxy!" form drives a bang generation job (Path A), and the gameserver'sBangImportServicetranslates its output and persists it. The in-processPOST /admin/galaxy/generateendpoint returns HTTP 410. The contract documented below is the canonical bang↔gameserver boundary; the fully standalone-service split, the completeUniverseJSON envelope, and thebang.*PostgreSQL schema remain launch-target. See../SYSTEMS/galaxy-generation.md.
sw2102-bang is the standalone universe generator that produces a fresh galaxy. The gameserver consumes its output to bootstrap (or rebuild) the world. This page documents the contract between the two repos.
The generator lives at https://github.com/.../sw2102-bang. The gameserver is services/gameserver/ inside the Sectorwars2102 repo. The two communicate by writing to and reading from the shared PostgreSQL database; there is no HTTP coupling.
Two execution modes¶
sw2102-bang supports two entry points. Both produce the same Universe shape internally; they differ in where the result goes.
CLI mode — JSON to disk or stdout¶
bigbang [options]
The CLI writes the generated universe as JSON to stdout, an output file, or as a human summary. Use this for review, reproducibility checks, and offline tooling.
Source: sw2102-bang/src/cli.ts
| Flag | Default | Meaning |
|---|---|---|
--sectors, -s <n> |
1000 | Total sectors (clamped 20-20000) |
--seed <n> |
random | RNG seed; same seed + same config = same universe |
--density <n> |
20 | Max course length, controls overall density (3-50). Recorded on Universe.config; enforcement semantics are pinned in a follow-up doc PR alongside bang's first enforcement implementation. |
--two-way-warps <n> |
30 | Two-way warp percentage (5-100). Drives the FORCED bidirectional bucket size; the total observed bidirectional fraction also includes natural k-NN symmetry from the bulge density gradient and is reported in --summary. |
--one-way-warps <n> |
5 | One-way warp percentage (0-25). Drives the FORCED one-way bucket size; the remainder of pairs preserves natural k-NN symmetry. |
--max-warps <n> |
6 | Max warps per sector (2-10). Strictly enforced via post-categorization drop; bidirectional warps count toward both endpoints' degree, one-way warps only toward the source. |
--port-percent <n> |
50 | Sectors with ports (0-80) |
--planet-percent <n> |
20 | Sectors with planets (0-60) |
--nebula-percent <n> |
5 | Sectors in nebulae (0-30) |
--fedspace <n> |
10 | Federation-protected sectors (2-20) |
--stardock <n> |
0 (auto) | Stardock sector ID; 0 means auto-pick |
--output, -o <file> |
stdout | Write JSON to file |
--summary |
off | Print a human-readable summary to stderr |
--render |
off | Print an ASCII map of the galaxy to stderr (3D → 2D projection with chalk colors). Density-binned grid; fedspace highlighted; cell-color gradient by sector count. |
--render-axis <axis> |
top | Projection axis for --render: top (XY plane), side (XZ plane), or iso (isometric). |
--no-color |
off | Disable chalk color codes in --render output. Useful for non-tty redirects, CI logs, and snapshot tests. |
--compact |
off | Compact JSON (no formatting) |
--quiet, -q |
off | Suppress progress output on stderr |
The same options resolve through resolveConfig() (sw2102-bang/src/config.ts), which clamps inputs and auto-picks a seed when seed === 0.
Service mode — write directly to PostgreSQL¶
The generator can also run as a one-shot container that writes to a database and exits. This is the integration path with the gameserver.
docker compose up generator
Source: sw2102-bang/src/db-writer.ts, sw2102-bang/Dockerfile.generator, sw2102-bang/docker-compose.yml
Configuration is via environment variables:
| Env var | Default | Meaning |
|---|---|---|
DATABASE_URL |
postgresql://tw2102:tw2102@localhost:5432/tw2102 |
Connection string |
UNIVERSE_NAME |
default |
Identifier; reuse to overwrite |
TW_SECTORS |
1000 |
Total sectors |
TW_SEED |
random | RNG seed |
TW_PORT_PERCENT |
50 |
Port density |
TW_PLANET_PERCENT |
20 |
Planet density |
The container waits for the database to accept connections, generates a universe, deletes any existing universe with the same name, and writes the new one in a single transaction.
Output shape — JSON¶
The CLI emits the Universe interface from sw2102-bang/src/types.ts, with the internal sectors map serialized as a keyed object. Per ADR-0069, bang emits a fully-formed region snapshot: the topology graph plus the named clusters, special formations, station and planet inventories, citadel content, and NPC rosters that go with it. Faction-influence values, the Region row, cross-region warp wiring, and runtime state remain gameserver-side.
{
"version": "1.1.0",
"seed": 42,
"totalSectors": 1000,
"sectors": {
"1": {
"id": 1,
"position": {"x": 0, "y": 0, "z": 0},
"warps": [2, 3, 7],
"port": {
"name": "Capital Station",
"class": 1,
"commodities": {
"fuel_ore": {"action": "B", "quantity": 1000, "capacity": 5000, "regenRate": 100},
"organics": {"action": "B", "quantity": 800, "capacity": 3000, "regenRate": 80},
"equipment": {"action": "S", "quantity": 500, "capacity": 2000, "regenRate": 50}
}
},
"planets": [
{
"name": "Terra",
"type": "earth",
"owner": null,
"fuelOre": 0,
"organics": 0,
"equipment": 0,
"colonists": 0,
"citadel": {
"level": 1,
"droneCapacity": 100,
"safeContents": {
"credits": 0,
"items": []
}
}
}
],
"navHazards": [
{"type": "mine", "owner": null, "quantity": 4}
],
"nebula": null,
"beacon": null,
"explored": true
}
},
"warps": [
{"from": 1, "to": 2, "oneWay": false}
],
"clusters": [
{
"id": 1,
"name": "Gateway Plaza",
"sectorRangeStart": 1,
"sectorRangeEnd": 250,
"type": "TRADE_HUB"
}
],
"specialFormations": [
{
"id": "f-001",
"type": "BUBBLE",
"anchorSectorId": 412,
"interiorSectorIds": [413, 414, 415, 416],
"properties": {
"interiorSize": 4,
"linkTunnelDepth": 0
}
},
{
"id": "f-002",
"type": "LOST_SECTOR",
"anchorSectorId": 731,
"interiorSectorIds": [731],
"properties": {
"exitWarp": null
}
},
{
"id": "f-003",
"type": "LOST_CLUSTER",
"anchorSectorId": 812,
"interiorSectorIds": [812, 813, 814, "...", 833],
"properties": {
"interiorClusterId": 7,
"exitWarp": { "sourceSectorId": 820, "destinationSectorId": 504 }
}
},
{
"id": "f-004",
"type": "ARCHIPELAGO",
"anchorSectorId": 850,
"interiorSectorIds": [850, 851, "...", 949],
"properties": {
"memberClusterIds": [8, 9, 10],
"crossWarpCount": 5,
"exitWarps": [
{ "sourceSectorId": 905, "destinationSectorId": 612 }
]
}
}
],
"npcRosters": [
{
"kind": "federation_marshal",
"factionCode": "terran_federation",
"targetCount": 9,
"hostSectorId": 5,
"namePool": ["..."]
}
],
"specialLocations": [
{"type": "terra", "sectorId": 1},
{"type": "stardock", "sectorId": 173},
{"type": "rylan", "sectorId": 401},
{"type": "alpha_centauri", "sectorId": 9},
{"type": "fringe_homeworld", "sectorId": 600}
],
"fedspaceSectors": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
"config": { "...": "the BigBangConfig used to generate this universe" },
"createdAt": "2026-04-30T18:00:00.000Z"
}
Field reference (from sw2102-bang/src/types.ts):
- Sectors. Each sector has a
position(integer 3D coordinates scaled by 10000, range roughly[-10000, 10000]),warps(an array of destination sector IDs), an optionalport, an array ofplanets, an array ofnavHazards, an optionalnebula, an optionalbeacon, and anexploredflag. - Ports. Class 0 is reserved for special ports (Stardock); classes 1-8 encode buy/sell combinations across the three core commodities. Each commodity tracks
action(Bfor buying,Sfor selling),quantity,capacity, andregenRate. Per ADR-0069,quantityis the initial seeded value produced by bang's deterministic seeding pass (the gameserver translator passes pricing and seeding constants in as inputs so balance tuning stays gameserver-controlled, but the seed run itself executes inside bang). - Planets. Six types (
barren,earth,mountainous,oceanic,glacial,volcanic). Generator output carries the planet's initial inventory in the flatfuelOre,organics,equipment, andcolonistsfields (per ADR-0069, bang owns the seeded values) and, when a citadel is stamped, the citadel'slevel,droneCapacity, andsafeContents(the safe's credits + item list — bang seeds an empty safe by default; populated safes are an operator-or-quest concern). - Citadels. Embedded inside each planet under
citadel.levelmatches the existing field;droneCapacityfollows the per-level table in../FEATURES/planets/citadels.md;safeContentsis the safe's worldgen contents (bang stamps an empty safe; named-safe quests populate this on the gameserver side). - Nav hazards.
mine(Armid),limpet, orfighter(deployed); each carries an owner ID and quantity. Mine quantities per sector are bang's responsibility (already in scope; reaffirmed by ADR-0069). - Nebulae.
normal(hides contents) ormagnetic(disrupts navigation); 1-100 density. - Warps. A flat list paralleling each sector's
warpsarray; each entry specifiesfrom,to, andoneWay. - Clusters. One row per cluster in the region —
name(AI-generated per ADR-0044),sectorRangeStart/sectorRangeEnd, andtype(one of the standard cluster-type enum values). No faction-influence values — those depend on the gameserver's faction enum and stay gameserver-side, seeded post-import from the per-zone profile in../SYSTEMS/bang-import-pipeline.md. - Special formations. One row per stamped formation —
type(one of the 12 catalog values:BUBBLE,DEAD_END_BUBBLE,GOLD_BUBBLE,TUNNEL,DEAD_END,WARP_SINK,BACKDOOR,BLISTER,ESCAPE_HATCH, plus the lost-formation setLOST_SECTOR/LOST_CLUSTER/ARCHIPELAGOadded in ADR-0070),anchorSectorId,interiorSectorIds, and a type-specificpropertiesJSONB. Bang stamps lost formations first (Step 6.5a), then the rest of the catalog (Step 6.5b) between proximity-warp placement and long-distance-tunnel placement; long-distance tunnels (Step 7) skip lost-formation sector sets. Bang enforces invariants — notably ADR-0046's "noWARP_SINKinside aBUBBLE" and ADR-0070's "no external warps from a lost formation except the optional one-way exits" — internally so output is correct-by-construction. The gameserver's Phase 13 validation gate cross-checks the same invariants on import. - NPC rosters. One row per worldgen-stamped roster:
kind(e.g.,federation_marshal,nexus_sentinel,pirate_captain),factionCode,targetCount,hostSectorId, and anamePoolfor runtime spawning. Bang emits the roster shapes specified by../FEATURES/gameplay/police-forces.mdPhase 12.5a (region rosters) and Phase 12.5b (Nexus rosters). NoNPCCharacterrows — runtime materialization (Phase 12.5c) stays gameserver-side vianpc_scheduler.bootstrap_region(region_id)after the import transaction commits. - Special locations. Terra, Stardock, Rylan, Alpha Centauri, Fringe Alliance homeworld; the generator places each one once.
- Fedspace. Sectors 1 through
fedspaceSize; protected from PvP.
Output shape — PostgreSQL¶
When run in service mode, the generator writes to the bang schema defined in sw2102-bang/db/init.sql:
| Table | Purpose | Key columns |
|---|---|---|
universes |
One row per generated universe. | id PK, name UNIQUE, version, seed, total_sectors, config (JSONB) |
sectors |
One row per sector. | id PK, universe_id FK, sector_number, beacon, explored |
warps |
Connections between sectors. | id PK, universe_id FK, from_sector, to_sector, one_way |
clusters |
One row per cluster (named, sector-range bounded). | id PK, universe_id FK, name, type, sector_range_start, sector_range_end |
special_formations |
One row per stamped formation. | id PK, universe_id FK, type, anchor_sector_id FK, interior_sector_ids (ARRAY), properties (JSONB) |
ports |
One row per port. | id PK, sector_id FK, name, class, fuel_ore (JSONB — quantity is the initial seeded value), organics (JSONB), equipment (JSONB) |
planets |
One row per planet. | id PK, sector_id FK, name, type, owner, commodity columns (fuel_ore/organics/equipment/colonists — initial seeded values), citadel_level, citadel_drone_capacity, citadel_safe_contents (JSONB) |
nebulae |
One row per nebula sector. | id PK, sector_id FK, type, density |
nav_hazards |
One row per hazard placement. | id PK, sector_id FK, type, quantity, owner |
npc_rosters |
One row per worldgen-stamped roster. | id PK, universe_id FK, kind, faction_code, target_count, host_sector_id FK, name_pool (JSONB) |
special_locations |
Terra/Stardock/etc. | id PK, universe_id FK, type, sector_number |
The writer deletes any prior universes row with the same name (cascading to all dependent rows) and writes the new universe in a single BEGIN/COMMIT transaction with batched inserts (500 rows per batch).
Gameserver consumption¶
The bang schema does not match the gameserver's domain schema. The gameserver expects Galaxy, Region, Cluster, Zone, Sector, Planet, Station, WarpTunnel, SpecialFormation, NPCRoster rows with multi-region scaffolding (Central Nexus, Terran Space, player-owned regions), faction-influence values, security/development/traffic levels, and the Sectorwars-specific eight-commodity catalog.
Per ADR-0069, bang emits a fully-formed region snapshot — clusters, formations, station and planet inventories, citadel content, and NPC rosters all arrive in the payload. The gameserver translator's job is therefore narrower than a content-derivation pipeline: it validates the payload against Phase 13 invariants, scaffolds the rows that depend on per-customer state (the Region row plus its operator config: subscription tier, governance, tax rate, language pack, aesthetic theme), glues the imported entities into the multi-region world (cluster faction-influence percentages, cross-region warp-gate wiring, Nexus attachment), and translates the 3-commodity bang port output into the gameserver's 8-commodity catalog (a real translation, not a regeneration). It does not derive clusters, stamp formations, or seed inventories — those arrive ready in the payload.
The translation algorithm — numbered steps with explicit per-entity translation tables, JSONB defaults, idempotency rules, and failure modes — is specified in ../SYSTEMS/bang-import-pipeline.md. All three operational paths below invoke the same translator pipeline; they differ only in how the bang payload reaches the translator and where the trigger comes from.
Path A — admin-triggered import¶
The admin endpoint POST /admin/galaxy/import-bang (see Swagger at <api-host>/docs) is the operator-facing trigger. The handler:
- Receives the admin's parameters (target region context, sector count, seed, density, faction balance, optional overrides).
- Invokes the generator out-of-process (CLI mode with the requested params), capturing the JSON.
- Hands the captured JSON plus the target region context to the translator pipeline.
- Returns the new
Galaxy.idandRegion.idto the admin.
Used for: post-launch region creation, admin-driven re-imports, and player-region provisioning. Because the import takes 60–120 seconds for a 5,000-sector Nexus, the admin endpoint runs the translator as an async background job and returns a job ID; the admin UI polls the job until completion.
Path B — bootstrap script¶
For first-time environment setup, the generator runs as a one-shot container that writes to a separate bang database (see "Service mode" above). A bootstrap script then reads bang.universes plus dependent rows, hands them to the translator pipeline, and exits. The script lives at services/gameserver/scripts/bootstrap_from_bang.py (target). It accepts a bang connection URL, a target Galaxy.name, and the target region context, and runs once per region during environment bring-up — typically twice on a fresh install (central_nexus then terran_space).
Used for: initial environment provisioning, full-galaxy re-rolls during major releases.
Path C — Alembic data migration¶
For test fixtures and CI environments, the generator output is checked in as a JSON artifact under services/gameserver/tests/fixtures/bang/ (target) and replayed via an Alembic data migration. The migration calls the same translator pipeline with the JSON loaded from disk. Suitable for deterministic tests; unsuitable for production-sized galaxies due to file size and migration time.
Used for: integration test fixtures, deterministic CI seeds.
Per-entity translation¶
The full per-entity translation (Sector, Warp, Port → Station, Planet, Nebula → Cluster nebula classification, NavHazard, SpecialLocation, fedspaceSectors, Cluster → Cluster, SpecialFormation → SpecialFormation, NPCRoster → NPCRoster) is documented in the appendices of ../SYSTEMS/bang-import-pipeline.md:
- Appendix A — bang
Commodity(3) → gameserver commodity (8) with default values for the synthesized five. - Appendix B — bang
NebulaeType(2) → gameserver nebula classification (6) by zone bias and density. - Appendix C — bang
PlanetType(6) → gameserverPlanetTypeenum. - Appendix D — bang
SpecialLocationType→ gameserver Sectorspecial_featuresand associated rows. - Appendix E — bang Port
class0–8 → gameserverStationClassandStationType. - Appendix F — bang
Cluster(named, sector-range-bounded) → gameserverClusterrow with seeded faction-influence overlay. - Appendix G — bang
SpecialFormation→ gameserverSpecialFormationrow (1:1 type-and-properties copy; gameserver validates). - Appendix H — bang
NPCRoster→ gameserverNPCRosterrow, with Phase 12.5cbootstrap_regionhook running post-commit.
This operations doc does not duplicate the appendices; consult the SYSTEMS doc when implementing any of the three paths.
Reproducibility¶
Both modes are deterministic given a seed and a BigBangConfig. The same seed always produces the same universe regardless of when or where the generator runs. Operators wanting to reproduce a specific galaxy should record the seed and the full config from the universe row.
Versioning¶
The version field in the Universe and universes row is the generator schema version. The gameserver's translator must check this version against its supported-version allowlist and refuse to import an unknown one. Per ADR-0069, the contract is load-bearing: bang and the gameserver release in lockstep on contract bumps. Adding a field, changing a field's shape, or relocating an invariant from one side of the boundary to the other is a contract change and bumps the version.
Supported versions:
| Version | Status | Notes |
|---|---|---|
1.0.0 |
Legacy | Original shape; no per-sector position field. Translator at bang-import-pipeline.md step 7 synthesizes 3D coordinates from a Hilbert-curve packing of sector-number ranges. |
1.1.0 (and 1.1.0-pre.x pre-releases) |
Current | Adds Sector.position: {x, y, z} integer fields scaled by 10000. Translator step 7 consumes position directly; no Hilbert packing. Walking-skeleton releases of sw2102-bang emit 1.1.0-pre.0 to flag that content layers (ports, planets, nebulae, special locations) may be stubbed. |
Pre-release suffixes (1.1.0-pre.0, 1.1.0-pre.1, …) parse as >= 1.1.0 for the position-consumption rule but do not satisfy gameserver Phase 13 invariants that depend on content layers (e.g., #8 one TERRA at Capital, #9 two SpaceDocks). Pre-release imports are operator-only and require --allow-incomplete-content on the import endpoint (target).
Known gaps¶
🐛 Commodity coverage — Bang emits 3 of 9 core commodities¶
The Bang generator's port-class system uses a binary 2^3 = 8 encoding across three commodities only:
fuel_ore(maps to gameserverore)organicsequipment
The gameserver spec defines nine core commodities for procedural trade per ../SYSTEMS/market-pricing.md COMMODITY_PRICE_RANGES. Per ADR-0062 E-D2, bang must cover the full set across appropriate station classes:
| Commodity | In Bang? | Station class affinity |
|---|---|---|
| ore | ✅ (fuel_ore) |
All classes 1+ |
| organics | ✅ | All classes 1+ |
| equipment | ✅ | Classes 2+ |
| fuel | ❌ | All classes 1+ |
| gourmet_food | ❌ | Classes 3+ |
| precious_metals | ❌ | Classes 3+ |
| exotic_technology | ❌ | Classes 4+ |
| luxury_goods | ❌ | Classes 4+ |
| colonists | ❌ | Class 5 only (mega-station) |
Per-class commodity coverage (per ADR-0062 E-D2):
| Station class | Commodities (procedural seed) |
|---|---|
| Class 1 (Frontier outpost) | ore, organics, fuel |
| Class 2 (Border trade post) | ore, organics, fuel, equipment |
| Class 3 (Industrial hub) | ore, organics, fuel, equipment, precious_metals, gourmet_food |
| Class 4 (Capital trade dock) | All except colonists |
| Class 5 (Nexus mega-station) | All 9 |
Bang validates coverage at generation time and fails the seed if any station class is missing a required commodity.
Effect: A Bang-bootstrapped universe has no procedural ports buying or selling 4 of the 7 core commodities. Players can only acquire those four through:
- Special locations (Stardock near the Capital Sector, TradeDocks).
- Refining or production from owned planets.
- Random events.
This is not the intended Launch state.
Resolution paths:
- Expand Bang — extend
sw2102-bang/src/types.tsCommodityenum to include all seven, and update the port-class generator to use a 2^7 = 128 encoding (or a curated subset). Higher fidelity but a real engineering effort. - Post-process at import — keep Bang's 3-commodity output and inject the missing four during the
bang-import-pipelinetranslator pass (see../SYSTEMS/bang-import-pipeline.md). Cheaper, but risks divergence between Bang's deterministic output and the gameserver's post-injected state.
The design target is option (1); option (2) is the practical near-term workaround pending Bang refactor.
Tracking: This gap is also flagged at ../FEATURES/economy/trading.md under "Bang generator commodity gap". When resolved, update both docs.
Related¶
- The translator algorithm itself:
../SYSTEMS/bang-import-pipeline.md. Sixteen numbered steps, explicit JSONB defaults, invariants, failure modes, and full per-entity translation tables. - Universe data model on the gameserver side:
../DATA_MODELS/galaxy.md. - JSONB shapes the translator must produce:
../DATA_MODELS/jsonb-schema.md. - The admin endpoint that triggers Path A:
POST /admin/galaxy/import-bang(see live Swagger at<api-host>/docs). - Multi-region scaffolding the translator must establish:
./multi-regional.md. - Central Nexus cluster organization invoked by the translator on Nexus imports:
../SYSTEMS/central-nexus-clusters.md. - Broader generation pipeline that wraps this translator:
../SYSTEMS/galaxy-generation.md.