Turns and maintenance

Follow into
Save into

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.

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.

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.