twansi is a Tradewars-inspired ANSI terminal multiplayer space game with a serverless peer mesh (no central game server). It runs in a terminal, uses a curses dashboard, and supports optional headless agent control.
- ANSI dashboard (20 FPS target) with radar, live events, metrics, market/tech summaries
- P2P UDP mesh with:
- reliability layer (ACK + retransmit)
- epoch + HMAC authentication
- policy-enforced min/max protocol version
- gossip-style event fanout with hop limit
- Bootstrap discovery:
- LAN UDP broadcast
HELLO - manual seeds (
--seed host:port) - HTTPS bootstrap seeds (default
https://twansi.trentwyatt.com/bootstrap.json) - optional DNS SRV
_twansi._udp.<domain>ifdnspythonis installed
- LAN UDP broadcast
- Persistence:
- SQLite world state + append-only event log
- offline digest cursor (catch-up summary)
- Core gameplay:
- sectors with warp graph (ring + extra links)
- deterministic world generation per
shard+protocol_epoch(topology/ports match across nodes) - action points (AP) with time-based regeneration and action costs
- movement simulation (for radar animation)
- mining + economy ticks
- station markets with per-sector stock and prices (buy/sell ore/gas/crystal)
- ports (Tradewars-style B/S classes) with bid/ask pricing; port inventory deltas converge across the mesh
- tech tree (tiered):
ship_hull,weapons,shields,mining,defense_grid - shields + shield regeneration
- sector ownership + sector defenses (owners can upgrade defense level)
- combat v2 (doctrine triangle + weapon/defense/shield + sector defense bonus)
- Optional local agent API (JSON-lines over TCP, localhost only)
cd /home/arduino/tradewars-ansi-p2p
python3 -m venv .venv
. .venv/bin/activate
pip install -e .cd /home/arduino/tradewars-ansi-p2p
TWANSI_HOME=$PWD/.data \
twansi init --nick captain --listen 0.0.0.0:39000 --shard alpha
TWANSI_HOME=$PWD/.data \
twansi runWorld consistency note:
- The galaxy (sectors/warps/ports) is generated deterministically from
--shardandtwansi_policy.jsonprotocol_epoch. - If you previously ran older builds that created a different map, delete your
TWANSI_HOMEdirectory and re-initto avoid mismatches.
Terminal 1:
cd /home/arduino/tradewars-ansi-p2p
TWANSI_HOME=$PWD/.p1 twansi init --nick alpha --listen 0.0.0.0:39010 --shard alpha --seed 127.0.0.1:39011
TWANSI_HOME=$PWD/.p1 twansi runTerminal 2:
cd /home/arduino/tradewars-ansi-p2p
TWANSI_HOME=$PWD/.p2 twansi init --nick beta --listen 0.0.0.0:39011 --shard alpha --seed 127.0.0.1:39010
TWANSI_HOME=$PWD/.p2 twansi runTWANSI_HOME=$PWD/.data TWANSI_DISABLE_UI=1 twansi run1HUD,2map,3players,4trade,5alliance,6chattopen global chat input,lopen local (sector) chat input,/open command inputqquithtoggle help text+/-radar zoom in/outmmine burst (AP cost)aattack random target (AP cost) or/attack <idprefix>to targetsscan/ping peersiinvite a peer to your alliance (AP cost)doffline digest summaryb/nbuy/sell ore (AP cost)f/rbuy/sell gas (AP cost)c/vbuy/sell crystal (AP cost)B/N/F/R/C/Vopen a prefilled buy/sell command you can edit (for custom quantities)uupgrade next available tech tier (AP cost)jjump to another sector (cheaper if it’s a direct warp; AP+gas cost)gupgrade sector defenses (only if you own the sector; AP+resources)
Missions:
- Shown on the map screen (
2). They rotate every ~5 minutes (deterministic per shard/epoch). survey: scan (s) in the mission sectorraid: win a battle in the mission sectorsupply: sell cargo in the mission sector
Slash commands:
/say <text>global chat/local <text>sector-local chat/attack <idprefix>targeted combat (same sector)/jump <sector>/buy <res> <qty>,/sell <res> <qty>/all create <name>,/all rename <name>,/all leave,/all kick <idprefix>/help
The metrics panel already surfaces shared market prices plus station and port totals while the help overlay (h) reiterates these keys. s logs warp neighbors, current sector ownership/defense, and whether a port exists so you can spot trade targets before jumping. Trades triggered with b/n, f/r, or c/v are routed through the local port when one is present, otherwise they fall back to the per-sector station market highlighted on the dashboard.
- A shared shard name plus the
protocol_epochfromtwansi_policy.jsonseed every nodeʼs galaxy topology and ports via_seed64("twansi-map", shard, epoch, sectors)so sectors, warps, and port placements converge without a central authority. - Warp rails always form a base ring (
ensure_mapaddss -> s+1plus extra random links seeded with the same deterministic RNG) andstore.configure_worldapplies the shard/epoch before any topology or market state writes. - Ports use
_seed64("twansi-port-class"...)to pick a Tradewars-styleB/Sclass and_seed64("twansi-port-stock"...)for balanced inventory, whiledrift_marketand_det_shiftkeep the global market prices in sync by advancing a per-minute slot hash tied to the shard, epoch, and resource. - If you change
--shardor bumpprotocol_epoch, wipe yourTWANSI_HOMEand re-runtwansi initso every node rebuilds the same deterministic world and market.
- Ports encode a three-letter class such as
BBSorSSB, where eachB/Sletter determines whether the port prefers buying or selling ore, gas, or crystal.port_infouses the class, current inventory, and the shared market base price to calculate bid/ask spreads that favor either selling into the port or buying from it. - When you trade with
game.nodecommands, the code prefers a local port (if present) before falling back to the station market. Port trades emit buy/sell events that adjust the shared per-sectorport_inventory, letting every peer converge on the same supply/demand deltas. - Station markets (
station_market) compute prices from the shared base market plus a sector-specific modifier (richness/danger and stock level) and also track inventory deterministically via_seed64("twansi-station-stock"...). Their fees are slightly higher than ports to keep the flashy ports enticing. - Market drift (
drift_market) updates the in-memory market price every resource tick and writes it throughstore.update_market_price, so even headless nodes (or scale smoke) experience the same minute-by-minute price swings.
When a node is running, a local TCP JSON-lines API is exposed on:
127.0.0.1:<listen_port + 100>
Requests:
{"cmd":"observe"}{"cmd":"digest"}{"cmd":"act","action":"mine|attack|scan|invite|buy|sell|upgrade|jump|defend|chat|alliance_create|alliance_rename|alliance_leave|alliance_kick|observe","args":{...}}
Example actions:
- Buy gas:
{"cmd":"act","action":"buy","args":{"resource":"gas","qty":5}} - Sell ore:
{"cmd":"act","action":"sell","args":{"resource":"ore","qty":10}} - Targeted attack:
{"cmd":"act","action":"attack","args":{"target":"deadbeef"}} - Chat:
{"cmd":"act","action":"chat","args":{"channel":"global","text":"hi"}} - Upgrade a specific domain:
{"cmd":"act","action":"upgrade","args":{"domain":"weapons"}} - Jump to a sector:
{"cmd":"act","action":"jump","args":{"sector":7}} - Upgrade defenses:
{"cmd":"act","action":"defend"}
Responses:
{"ok":true,"result":...}{"ok":false,"error":"..."}
Run included bot harness (talks to agent API):
twansi bot --host 127.0.0.1 --port 39100 --seconds 30The game reads twansi_policy.json from repo root at runtime.
Key fields:
min_protocol_version,max_protocol_version: receivers drop packets outside this rangeprotocol_epoch: receivers drop packets with a different epochreliable_event_types: which events are sent reliablymax_event_hops: limits gossip amplification
Important note:
- Epoch+HMAC prevents “casual bypass” only if you provide a private secret. A purely public shard cannot prevent malicious clients from reimplementing the protocol.
Optional private salt:
- Set
TWANSI_SHARD_SECRETon nodes you control. It is mixed into the shard HMAC key derivation.
Default HTTPS bootstrap:
TWANSI_BOOTSTRAP_URLdefaults tohttps://twansi.trentwyatt.com/bootstrap.json
DNS SRV (optional):
- Create SRV records for
_twansi._udp.twansi.trentwyatt.com - Requires
dnspythoninstalled for clients to use SRV discovery
Manual:
- Use
--seed host:portontwansi init
./bin/run-tests
./bin/continuous-smoke 75
./bin/scale-smoke 8 60 39600Two players:
./bin/demo-federation-tmux twansi-demo
tmux attach -t twansi-demoFour players + 4 bots:
./bin/demo-4p twansi-4p
tmux attach -t twansi-4pAgent API docs:
docs/agent_api.md