select navigate esc close

Turns and maintenance

twarp ·

The clock runs the game now. The turns & maintenance epic is merged: the daily maintenance pass — the original's "Extern" — is a scheduled, journaled command instead of an admin verb, turns are a live economy with a daily allotment and an hourly credit, the overnight tax on carried credits applies at the day boundary, and a client's event cursor survives a server restart. The last milestone gave the game an economy; this one makes it a persistent world that no longer needs anyone to run it.

The Extern

In the original, an external maintenance program ran once a day — canonically at midnight, scheduled by the sysop — and put the universe in order while the board sat idle. The Trade Wars Bible calls it out directly ("External Maintenance … run at midnight"), and TW2001's ancestral MAINT.PAS implements the same phase. Its duty roster, from the Bible, the sysop docs, and that source, against what twarp runs today:

Extern duty (original) Status in twarp
Advance the game day Live — the Extern's first act
Port restock toward maximum Live — 5% of max per day, the v3.34-attested default
Reset every trader's turns Live — this epic
Overnight tax on carried credits Live — this epic
FedSpace cleanup (tow violators out) Awaits combat & deployables
Major Space Lane sweep (clear deployables) Awaits combat & deployables
Cloak failure rolls Awaits combat & deployables
Planet production, colonist growth Awaits planets & citadels
Citadel construction progress Awaits planets & citadels
Planet collisions ("Unstable Planetary Mass") Awaits planets & citadels
Bust clearing Awaits port robbery (deferred from ports & trading)
NPC upkeep (Ferrengi regeneration) Awaits NPCs & factions
Inactive player deletion Awaits operations

The unbuilt rows are reserved seats: the midnight pass now exists as infrastructure, and each future epic adds its chores to it.

Structurally, twarp's Extern is not a separate program run against data files while the game is down — there is no "down". It is a journaled command like any player verb, enqueued by the scheduler under its own origin and applied by the same deterministic reducer that applies a warp or a haggle. Each pass increments the day, regenerates every port commodity, resets every trader's turns, applies the tax, stamps the maintenance anchor, and writes the daily log line:

Day 12 has dawned across the galaxy

The previous milestone shipped an admin-gated day verb so the acceptance flight could roll the clock by hand. This epic deleted it, along with the entire admin service that held it — the scheduler is now the only source of a new day. That is the epic's one non-additive protocol change, and it was made deliberately.

Turn accounting

Turns were the original's defining scarcity: a daily allotment, every meaningful action drawing against it, unused turns gone at the boundary. twarp now enforces the same ration. The allotment is a server setting (turns_per_day, default 250), and the costs are the attested ones:

Action Turn cost
Warp between sectors the ship's turns-per-warp (2–15 by hull)
Dock at a port 1
Holo scan 1
Density scan 0
Computer queries, CIM, route plotting 0

The first row is the one that changes the game. The shipyard catalog shipped last epic with a turns-per-warp column that was inert — every hull moved, none paid differently. With turn accounting live, the column governs, and ship choice becomes the trade-off it originally was:

Ship Turns/warp Holds Fighters Shields Base cost
Merchant Cruiser 3 75 2,500 400 26,300
Scout Marauder 2 25 250 100 13,200
Missile Frigate 3 60 5,000 400 28,800
Corellian BattleShip 4 80 10,000 750 40,500
Corporate FlagShip 3 85 20,000 1,500 71,000
Colonial Transport 6 250 200 500 54,400
CargoTran 4 125 400 1,000 59,400
Merchant Freighter 2 65 300 500 36,200
Imperial StarShip 4 150 50,000 2,000 128,600
Havoc GunStar 3 50 10,000 3,000 29,500
StarMaster 3 73 5,000 2,000 48,000
Constellation 3 80 5,000 750 40,500
T'Khasi Orion 2 60 750 750 36,000
Tholian Sentinel 4 50 2,500 4,000 27,000
Taurean Mule 4 150 300 600 53,600
Interdictor Cruiser 15 40 100,000 4,000 250,000

A Merchant Freighter moves 65 holds at 2 turns per warp; a Colonial Transport moves 250 at 6. Short circuits favor the Transport's capacity, long hauls the Freighter's mileage — the original's arithmetic, back in force. The Interdictor Cruiser keeps its notorious 15 turns per warp: a fortress that can barely move, as designed.

A line of merchant ships queued at an orbital refueling gantry beneath an enormous gauge dial

The hourly credit. Here the record thins and we had to make a call. The model attested nearly everywhere is a hard daily allotment, nothing more. One TWGS-era source adds, in passing, that turns regenerate at the top of the hour — and whether v3.34 actually had an hourly mode is one of our [U] marks, unrecovered pending dynamic analysis of the binaries. twarp ships a composite, tagged provisional in the source: the daily reset is the anchor, and each hour every trader below the allotment is credited max(1, turns_per_day / 24), hard-clamped at the allotment — 10 turns an hour at the default 250. The floor keeps small allotments regenerating at all; the clamp is the guarantee, and the test suite proves by saturation that no path lets a trader exceed the allotment. If the binaries later show v3.34 never trickled, the constant lives in one declared place and the hourly command simply stops being scheduled. Unused turns still do not bank.

The overnight tax. The rule that taught traders to use the bank: end the day carrying too many credits and the Federation takes its share. The recovered terms, and what twarp applies:

Parameter v1 preset TWGS default twarp
Tax threshold (carried credits) 50,000 100,000 100,000
Rate 10% 10% 10%
Bank balance exempt exempt exempt
Evil-aligned traders untaxed untaxed untaxed

At each Extern, a good-aligned trader (zero counts as good, per the recovered convention) carrying over 100,000 credits loses 10% of the carried amount. Bank deposits are untouched — dock, deposit, log off remains the correct ritual — and evil traders are exempt, as they always were.

The clock

The kernel stays deterministic and knows nothing of wall time — a replay next year must produce today's bytes, so the reducer can never consult the OS clock. The clock lives outside it, in the server process, as a policy layer with two timers: the daily Extern at a configured boundary (extern_time, an HH:MM setting, default 00:00 — midnight, as attested) and the hourly credit at the top of each hour. Each firing enqueues a journaled command; the kernel applies it like everything else. Replay never re-runs the scheduler — it re-reads what the scheduler did.

The interesting case is a server that was down when midnight passed. Every Extern stamps an anchor — the journaled receipt time, descended from the original's "Last EXTERN date" field — and at boot the scheduler reads it and asks one question: has a boundary passed since? If so, it owes exactly one catch-up Extern, run synchronously before the server accepts a connection. Not one per missed day: a server dark for thirty days advances one day, the same way a BBS unplugged for a month got one maintenance pass when it came back. A dormant world does not accrue. The tests plant a stale two-day anchor to prove the answer is one, that an immediate reboot owes nothing, and that a fresh universe owes nothing.

One fidelity detail worth recording: days advanced during the previous epic — by the now-deleted admin verb — sit in the journal as commands carrying no turn allotment, because the turn economy did not exist when they were written. They replay as they ran: day and restock only, no reset, no tax. The alternative was letting replay reinterpret the past, which an engine built on an append-only journal does not get to do.

An orbital station ring re-illuminating after darkness, a single ship holding position nearby

A restart, survived

The last piece looks inward. Every event in the world stream now carries the game day it happened on, stamped at apply time — the day a replay reconstructs is the day the live run emitted. The Extern increments the day before announcing it, so "Day 12 has dawned" is itself an event of day 12.

And the event stream survives the server. At boot, before anything else runs, the server replays its journal and rebuilds the full event history, so a cursor from a previous life still resolves. The acceptance gate was manual, on the live dev server: subscribe from genesis and watch 102 events replay — event 94 reading "Day 2 has dawned," stamped day 2 — restart the server process, resubscribe from cursor 102, and receive event 103, contiguous, no gap, no error. For a bot author — and bots are half the point of publishing a protocol instead of a screen — that means a watcher holds its cursor through server maintenance and misses nothing.

The standing invariant holds with it: stop the server mid-day and start it again, and credits, bank balances, cargo, turns, the day counter, and the Extern anchor come back byte-identical, because the world is the journal and the journal replays.

The protocol surface

The wire delta: the day-advance command now carries the daily allotment, a new hourly turn-credit command joins it, and every world event carries its game day. The one removal is the admin service — day is gone from the reference CLI with it — because the scheduler is now the sole source of maintenance, and the protocol baseline was deliberately rebuilt to say so. Everything a client sees remains the published schema: turn balances on the self readout, day numbers on the event stream, no screen scraping.

Next

Combat and deployables: attacks, fighters and dispositions, mines and limpets, tolls, escape pods. The Extern's reserved seats start to fill — FedSpace cleanup and the space-lane sweep join the midnight pass — and the quiet universe the clock now tends gets considerably less safe to sleep in.