0.8.0:0 — bundle Station Master v0.8.0

Watching the table: every accepted move, and every automatic phase that does
anything, is replayed on a player's own screen in the order it happened. The
Office Area follows whoever is acting, so another player's switching moves step
across their board instead of the position simply having changed.

Release notes in all five locales. No migration: the whole engine diff between
v0.7.9.8 and v0.8.0 is four GameEvent types gaining a `player` field, no
predicate changed its answer, and events are not persisted — so games in progress
on the server resume normally.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6cF1iYvJ1kNmzYBzu4QX6
This commit is contained in:
Jesse
2026-09-09 16:02:12 -04:00
co-authored by Claude Opus 5
parent 8f2f733a66
commit 3145e8543c
2 changed files with 106 additions and 109 deletions
+105 -108
View File
@@ -10,127 +10,124 @@ export const current = VersionInfo.of({
// — checked against the SDK's own parser rather than assumed, since every example in `versions.md` // — checked against the SDK's own parser rather than assumed, since every example in `versions.md`
// has three: `0.7.9.8:0` parses to upstream `[0,7,9,8]`, which sorts above `[0,7,9]`, so a box on // has three: `0.7.9.8:0` parses to upstream `[0,7,9,8]`, which sorts above `[0,7,9]`, so a box on
// 0.7.9:0 sees this as an update rather than refusing it as a downgrade. // 0.7.9:0 sees this as an update rather than refusing it as a downgrade.
version: '0.7.9.8:0', version: '0.8.0:0',
releaseNotes: { releaseNotes: {
en_US: en_US:
'Bundles Station Master v0.7.9.8 — eight releases of fixes since v0.7.9, aimed at multiplayer. ' + 'Bundles Station Master v0.8.0 — you can watch the other players now. GAMES IN PROGRESS ON ' +
'GAMES IN PROGRESS ON THIS SERVER ARE UNAFFECTED and will resume normally. Two things a table ' + 'THIS SERVER ARE UNAFFECTED and will resume normally. Until now another player could move a ' +
'could see that only one seat should have: the random seed was announced in the shared history, ' + 'train the length of their district and the only trace on your screen was a line of text: your ' +
'and a blind Home Office draw named the card it drew. The history panel came back EMPTY after a ' + 'board showed your own Office Area and nobody else’s, and a bot’s whole turn arrived as a ' +
'browser reload mid-game — the server had the whole log and was sending it in a field nothing ' + 'position that had simply changed. Now every move is replayed on your screen in the order it ' +
'read. During the end-of-game "play another Day?" vote the turn chart named one arbitrary ' + 'happened. The Office Area follows whoever is acting, so you watch their switching moves step ' +
'player, next to a tally correctly showing everyone still to vote; the vote is open to every ' + 'across their board — which is the part worth seeing, and the part reading the history could ' +
'seat at once, so it now says nobody is being waited on. A train stopped at your Limits by an ' + 'never convey. A row above the board says how many moves behind you are and counts down, with ' +
'Interlocking VANISHED off the board until an A/D track freed up — it is drawn at the Limits ' + 'a Skip button when you would rather not wait; nothing is lost by skipping, since every line ' +
'now, marked as held. Trains queued for a Crew Tray say so instead of waiting invisibly, with ' + 'is already in the History panel. The automatic phases — New Train, the Mainline, the shift ' +
'the number of free trays beside them. A Red Flag standing at an Office is drawn on the map. A ' + 'change — used to resolve between one click and the next without ever being drawn, and now ' +
'Telegraph, Telephone or Radio already used today is struck through instead of still ' + 'take a visible beat when something happens in them and none at all when nothing does. ' +
'advertising its bonus, and says so when another district holds the Fedora. The Campaign Train ' + 'Switching moves in the history finally say who made them and name the train, instead of ' +
'says whether its speeches are made, which is what decides whether it must be kept at the ' + 'reading as an anonymous “CREW moved”. Playback speed is a per-player setting and can be ' +
'station. Plus two bugs from the last playtest: the Express’s one-freight-car-per-location ' + 'turned off entirely.',
'rule is now explained where it refuses you, and westbound trains are drawn in the correct half ' +
'of a Mainline card.',
es_ES: es_ES:
'Incluye Station Master v0.7.9.8 — ocho versiones de correcciones desde la v0.7.9, centradas en ' + 'Incluye Station Master v0.8.0 — ahora puedes ver jugar a los demás. LAS PARTIDAS EN CURSO EN ' +
'el multijugador. LAS PARTIDAS EN CURSO EN ESTE SERVIDOR NO SE VEN AFECTADAS y se reanudarán con ' + 'ESTE SERVIDOR NO SE VEN AFECTADAS y se reanudarán con normalidad. Hasta ahora otro jugador ' +
'normalidad. Dos cosas que toda la mesa podía ver y solo un jugador debía: la semilla aleatoria ' + 'podía mover un tren a lo largo de todo su distrito y el único rastro en tu pantalla era una ' +
'se anunciaba en el historial compartido, y un robo a ciegas de la Oficina Central nombraba la ' + 'línea de texto: tu tablero mostraba tu propia Área de Oficina y la de nadie más, y el turno ' +
'carta robada. El panel de historial volvía VACÍO tras recargar el navegador a mitad de partida: ' + 'completo de un bot llegaba como una posición que simplemente había cambiado. Ahora cada ' +
'el servidor tenía el registro completo y lo enviaba en un campo que nadie leía. Durante la ' + 'movimiento se reproduce en tu pantalla en el orden en que ocurrió. El Área de Oficina sigue a ' +
'votación final de "¿jugar otro Día?" el turno nombraba a un jugador cualquiera, junto a un ' + 'quien está jugando, así que ves sus maniobras avanzar por su tablero, que es lo que merece la ' +
'recuento que sí mostraba a todos los pendientes; la votación está abierta a todos a la vez, así ' + 'pena ver y lo que el historial nunca podía transmitir. Una fila sobre el tablero indica ' +
'que ahora dice que no se espera a nadie. Un tren detenido en tus Límites por un Enclavamiento ' + 'cuántos movimientos llevas de retraso y va descontando, con un botón Saltar por si prefieres ' +
'DESAPARECÍA del tablero hasta que se liberaba una vía: ahora se dibuja en los Límites, marcado ' + 'no esperar; no se pierde nada al saltar, porque cada línea ya está en el panel de Historial. ' +
'como retenido. Los trenes en cola por una Bandeja de Personal lo indican, con el número de ' + 'Las fases automáticas — Tren Nuevo, la Línea Principal, el cambio de turno — se resolvían ' +
'bandejas libres al lado. Una Bandera Roja plantada en una Oficina se dibuja en el mapa. Un ' + 'entre un clic y el siguiente sin dibujarse nunca, y ahora tienen una pausa visible cuando algo ' +
'Telégrafo, Teléfono o Radio ya usado hoy aparece tachado en vez de seguir anunciando su bono, e ' + 'ocurre en ellas y ninguna cuando no ocurre nada. Las maniobras en el historial por fin dicen ' +
'indica cuándo el Sombrero lo tiene otro distrito. El Tren de Campaña dice si ya ha dado sus ' + 'quién las hizo y nombran el tren, en lugar de leerse como un anónimo «CREW moved». La ' +
'discursos, que es lo que decide si debe permanecer en la estación. Además, dos fallos del ' + 'velocidad de reproducción es un ajuste por jugador y se puede desactivar por completo.',
'último playtest: la regla de un vagón de carga por ubicación del Expreso se explica donde te ' +
'rechaza, y los trenes en dirección oeste se dibujan en la mitad correcta de la carta.',
de_DE: de_DE:
'Enthält Station Master v0.7.9.8 — acht Fehlerkorrektur-Versionen seit v0.7.9, mit Blick auf den ' + 'Enthält Station Master v0.8.0 — Sie können den anderen jetzt beim Spielen zusehen. LAUFENDE ' +
'Mehrspielermodus. LAUFENDE PARTIEN AUF DIESEM SERVER SIND NICHT BETROFFEN und werden normal ' + 'PARTIEN AUF DIESEM SERVER SIND NICHT BETROFFEN und werden normal fortgesetzt. Bisher konnte ' +
'fortgesetzt. Zwei Dinge, die der ganze Tisch sehen konnte und nur ein Platz sollte: der ' + 'ein anderer Spieler einen Zug durch sein ganzes Revier bewegen, und die einzige Spur auf ' +
'Zufallsstartwert stand im gemeinsamen Verlauf, und ein Blindzug aus dem Hauptbüro nannte die ' + 'Ihrem Bildschirm war eine Textzeile: Ihr Brett zeigte nur Ihren eigenen Dienstbereich und den ' +
'gezogene Karte. Der Verlauf kam nach einem Neuladen des Browsers mitten im Spiel LEER zurück — ' + 'von niemandem sonst, und der komplette Zug eines Bots erschien als eine Stellung, die sich ' +
'der Server hatte das ganze Protokoll und schickte es in einem Feld, das niemand las. Bei der ' + 'einfach geändert hatte. Jetzt wird jeder Zug auf Ihrem Bildschirm in der Reihenfolge ' +
'Abstimmung "noch einen Tag spielen?" nannte die Zuganzeige einen beliebigen Spieler, neben ' + 'wiedergegeben, in der er geschah. Der Dienstbereich folgt demjenigen, der am Zug ist, sodass ' +
'einer Zählung, die alle Ausstehenden korrekt zeigte; die Abstimmung steht allen zugleich offen, ' + 'Sie seine Rangierbewegungen über sein Brett wandern sehen — das ist der sehenswerte Teil und ' +
'also heißt es jetzt, dass auf niemanden gewartet wird. Ein von einem Stellwerk an deinen ' + 'der, den das Protokoll nie vermitteln konnte. Eine Zeile über dem Brett zeigt, wie viele Züge ' +
'Grenzen angehaltener Zug VERSCHWAND vom Brett, bis ein Gleis frei wurde — er wird jetzt an den ' + 'Sie im Rückstand sind, und zählt herunter, mit einer Überspringen-Schaltfläche, wenn Sie ' +
'Grenzen gezeichnet und als angehalten markiert. Züge, die auf ein Besatzungstablett warten, ' + 'nicht warten möchten; dabei geht nichts verloren, denn jede Zeile steht schon im Protokoll. ' +
'sagen das nun, mit der Zahl der freien Tabletts daneben. Eine an einem Büro stehende Rote Fahne ' + 'Die automatischen Phasen — Neuer Zug, die Hauptstrecke, der Schichtwechsel — liefen zwischen ' +
'erscheint auf der Karte. Ein heute schon genutzter Telegraf, ein Telefon oder Radio wird ' + 'einem Klick und dem nächsten ab, ohne je gezeichnet zu werden, und halten nun sichtbar inne, ' +
'durchgestrichen, statt weiter seinen Bonus anzupreisen, und sagt, wenn ein anderer Bezirk den ' + 'wenn darin etwas passiert, und gar nicht, wenn nichts passiert. Rangierbewegungen im ' +
'Hut hat. Der Wahlkampfzug sagt, ob seine Reden gehalten sind — das entscheidet, ob er am ' + 'Protokoll nennen endlich den Spieler und den Zug, statt als anonymes „CREW moved“ zu ' +
'Bahnhof bleiben muss. Dazu zwei Fehler aus dem letzten Playtest: die Ein-Güterwagen-pro-Ort- ' + 'erscheinen. Die Wiedergabegeschwindigkeit ist eine Einstellung pro Spieler und lässt sich ' +
'Regel des Express wird dort erklärt, wo sie dich abweist, und Züge nach Westen werden in der ' + 'ganz abschalten.',
'richtigen Kartenhälfte gezeichnet.',
pl_PL: pl_PL:
'Zawiera Station Master v0.7.9.8 — osiem wydań poprawek od v0.7.9, pod kątem gry wieloosobowej. ' + 'Zawiera Station Master v0.8.0 — teraz możesz obserwować grę pozostałych. TRWAJĄCE PARTIE NA ' +
'TRWAJĄCE PARTIE NA TYM SERWERZE NIE SĄ DOTKNIĘTE i wznowią się normalnie. Dwie rzeczy, które ' + 'TYM SERWERZE NIE SĄ NARUSZONE i zostaną normalnie wznowione. Do tej pory inny gracz mógł ' +
'widział cały stół, a powinien tylko jeden gracz: losowe ziarno ogłaszano we wspólnej historii, a ' + 'przeprowadzić pociąg przez cały swój okręg, a jedynym śladem na twoim ekranie była linijka ' +
'zakryte dobranie z Biura Głównego podawało nazwę dobranej karty. Panel historii wracał PUSTY po ' + 'tekstu: twoja plansza pokazywała wyłącznie twój własny Obszar Biura i niczyj inny, a cała ' +
'przeładowaniu przeglądarki w trakcie gry — serwer miał cały dziennik i wysyłał go w polu, ' + 'tura bota pojawiała się jako pozycja, która po prostu się zmieniła. Teraz każdy ruch jest ' +
'którego nikt nie czytał. Podczas głosowania "zagrać jeszcze jeden Dzień?" wykres tury wskazywał ' + 'odtwarzany na twoim ekranie w kolejności, w jakiej nastąpił. Obszar Biura podąża za tym, kto ' +
'przypadkowego gracza, obok licznika poprawnie pokazującego wszystkich pozostałych; głosowanie ' + 'wykonuje ruch, więc widzisz, jak jego manewry przesuwają się po jego planszy — a to właśnie ' +
'jest otwarte dla wszystkich naraz, więc teraz mówi, że nie czeka się na nikogo. Pociąg ' + 'warto zobaczyć i tego historia nigdy nie potrafiła przekazać. Wiersz nad planszą pokazuje, o ' +
'zatrzymany na twoich Granicach przez Nastawnię ZNIKAŁ z planszy do czasu zwolnienia toru — teraz ' + 'ile ruchów jesteś z tyłu, i odlicza, z przyciskiem Pomiń, jeśli wolisz nie czekać; pominięcie ' +
'jest rysowany na Granicach i oznaczony jako wstrzymany. Pociągi czekające na Tackę Załogi mówią ' + 'nic nie kosztuje, bo każda linijka jest już w panelu Historii. Fazy automatyczne — Nowy ' +
'o tym, z liczbą wolnych tacek obok. Czerwona Flaga stojąca przy Biurze jest widoczna na mapie. ' + 'Pociąg, Linia Główna, zmiana warty — rozstrzygały się między jednym kliknięciem a drugim, ' +
'Telegraf, Telefon lub Radio użyte już dzisiaj są przekreślone, zamiast wciąż reklamować swoją ' + 'nigdy nie będąc narysowane, a teraz zatrzymują się na widoczną chwilę, gdy coś się w nich ' +
'premię, i informują, gdy Kapelusz ma inny okręg. Pociąg Kampanijny mówi, czy przemówienia już ' + 'dzieje, i wcale, gdy nic się nie dzieje. Manewry w historii wreszcie mówią, kto je wykonał, i ' +
'wygłoszono — to decyduje, czy musi stać na stacji. Do tego dwa błędy z ostatniego playtestu: ' + 'nazywają pociąg, zamiast brzmieć jak anonimowe „CREW moved”. Szybkość odtwarzania jest ' +
'zasada jednego wagonu towarowego na lokalizację w Ekspresie jest wyjaśniana tam, gdzie cię ' + 'ustawieniem dla każdego gracza osobno i można ją całkiem wyłączyć.',
'blokuje, a pociągi na zachód są rysowane we właściwej połowie karty.',
fr_FR: fr_FR:
'Inclut Station Master v0.7.9.8 — huit versions de corrections depuis la v0.7.9, orientées ' + 'Contient Station Master v0.8.0 — vous pouvez maintenant regarder jouer les autres. LES ' +
'multijoueur. LES PARTIES EN COURS SUR CE SERVEUR NE SONT PAS AFFECTÉES et reprendront ' + 'PARTIES EN COURS SUR CE SERVEUR NE SONT PAS AFFECTÉES et reprendront normalement. Jusqu’ici ' +
'normalement. Deux choses que toute la table pouvait voir et qu’un seul joueur aurait dû : la ' + 'un autre joueur pouvait déplacer un train sur toute la longueur de son district et la seule ' +
'graine aléatoire était annoncée dans l’historique partagé, et une pioche à l’aveugle au Bureau ' + 'trace sur votre écran était une ligne de texte : votre plateau ne montrait que votre propre ' +
'Central nommait la carte piochée. L’historique revenait VIDE après un rechargement du ' + 'Zone de Bureau et celle de personne d’autre, et le tour entier d’un bot arrivait comme une ' +
'navigateur en pleine partie : le serveur avait tout le journal et l’envoyait dans un champ que ' + 'position qui avait simplement changé. Désormais chaque coup est rejoué sur votre écran dans ' +
'personne ne lisait. Pendant le vote « jouer un Jour de plus ? », le tableau des tours nommait un ' + 'l’ordre où il a eu lieu. La Zone de Bureau suit celui qui joue, si bien que vous voyez ses ' +
'joueur arbitraire, à côté d’un décompte affichant correctement tous ceux qui restaient à voter ; ' + 'manœuvres progresser sur son plateau — c’est la partie qui vaut d’être vue, et celle que lire ' +
'le vote est ouvert à tous à la fois, il indique donc maintenant qu’on n’attend personne. Un ' + 'l’historique ne pouvait jamais rendre. Une ligne au-dessus du plateau indique de combien de ' +
'train arrêté à vos Limites par un Enclenchement DISPARAISSAIT du plateau jusqu’à ce qu’une voie ' + 'coups vous êtes en retard et décompte, avec un bouton Passer si vous préférez ne pas ' +
'se libère — il est désormais dessiné aux Limites, marqué comme retenu. Les trains en attente ' + 'attendre ; rien n’est perdu en passant, puisque chaque ligne est déjà dans le panneau ' +
'd’un Plateau d’Équipe le disent, avec le nombre de plateaux libres à côté. Un Drapeau Rouge ' + 'Historique. Les phases automatiques — Nouveau Train, la Grande Ligne, la relève — se ' +
'planté à un Bureau apparaît sur la carte. Un Télégraphe, Téléphone ou Radio déjà utilisé ' + 'résolvaient entre un clic et le suivant sans jamais être dessinées, et marquent maintenant un ' +
'aujourd’hui est barré au lieu de continuer à annoncer son bonus, et signale quand un autre ' + 'temps visible quand quelque chose s’y produit, et aucun quand rien ne s’y produit. Les ' +
'district porte le Chapeau. Le Train de Campagne dit si ses discours sont faits, ce qui décide ' + 'manœuvres dans l’historique disent enfin qui les a faites et nomment le train, au lieu de se ' +
's’il doit rester en gare. Plus deux bugs du dernier playtest : la règle un wagon de fret par ' + 'lire comme un « CREW moved » anonyme. La vitesse de relecture est un réglage par joueur et ' +
'emplacement de l’Express est expliquée là où elle vous refuse, et les trains vers l’ouest sont ' + 'peut être entièrement désactivée.',
'dessinés dans la bonne moitié de la carte.',
}, },
migrations: { migrations: {
/** /**
* NOTHING TO CARRY, AND THIS TIME THAT REALLY IS "NOTHING BREAKS" — which is the opposite of * NOTHING TO CARRY, AND CHECKED THE SAME NARROW WAY v0.7.9.8's ENTRY WAS.
* v0.7.9, and was checked rather than hoped for.
* *
* v0.7.9 changed a RULE (§3.4's collision caps became live in solitaire), so a saved game that * The rule this package lives by is in the game's README: a save is a seed plus the moves
* had crossed one stopped replaying. The general rule that fell out of it is written down in the * played, replayed through the CURRENT rules, so the only thing that strands an older save is a
* game's README: a save is a seed plus the moves played, replayed through the CURRENT rules, so * change that makes a once-legal move illegal. So the question is never "how big was the
* any change that makes a once-legal move illegal stops an older save. * release" — v0.8.0 is a large one — but "did any predicate change its answer".
* *
* SO THE QUESTION FOR THIS BUMP IS NARROW: did anything become illegal between v0.7.9 and * Measured with `git diff v0.7.9.8..v0.8.0 -- src/engine/`. The WHOLE engine diff is eight lines:
* v0.7.9.8? Measured with `git diff v0.7.9..HEAD -- src/engine/` and reading every non-comment * four `GameEvent` types (`trayMoved`, `carsCoupled`, `carsDropped`, `consistSorted`) gained a
* line of it. The whole diff is: `isExpedited` exported and widened to a structural parameter * `player` field, and the four places that build them pass it. `check()` is untouched, `legal.ts`
* type, `isFreight` exported, one new read-only helper (`freightRuleSpentHere`, which the * and `advance.ts` are untouched, and no predicate anywhere was edited. Games in progress resume,
* blocked panel asks and nothing else), and `check('draw.end')`'s inline hand-limit test replaced * and the release notes say so in all five locales rather than leaving it to be discovered.
* by a call to `overHandLimit(state, player)` holding the identical expression. No predicate
* changed its answer. Eight releases of work, and none of it in the rules.
* *
* MULTIPLAYER SAVES LIVE ON THIS SERVER and are the ones this package can strand, so that * EVENTS ARE NOT PERSISTED, so widening four of them cannot invalidate anything on disk: a
* matters here in a way it did not for v0.7.9 — whose breakage was solitaire, in the player's own * `SavedGame` is `{ seed, config, history: Intent[], … }` and the events are derived by replaying
* browser, where nothing in this package could have acted on it either way. Games in progress * it. The narration TEXT of two lines changed with them ("Moved Train 3 …" where it used to read
* resume; the release notes say so in all five locales rather than leaving it to be discovered. * "CREW moved …"), which a resumed game simply regenerates — the log is not stored either.
* *
* WHAT IS NOT COVERED BY THAT ARGUMENT: `Frame` gained fields (`crewTrays`, `queued`, * WHAT IS NOT COVERED BY THAT ARGUMENT, and why it still does not need a migration: the wire
* `heldAtLimits`, `enhancementsSpent`) and lost the narration log it was carrying for nobody. * between this server and a browser gained a lot. `Push` now carries ordered presentation steps
* That is the wire between this server and a browser, not anything on disk — a browser reloads * and a public-board baseline, and the board deltas changed shape. None of it is on disk, and a
* onto the new page as part of the update, so there is no version skew to migrate. * browser reloads onto the new page as part of the update, so there is no version skew to
* migrate — the same reasoning v0.7.9.8 used for its own `Frame` changes.
*
* THE ONE THING THAT WOULD HAVE NEEDED CARE, had it been built the way the plan first described:
* the display-step collector runs inside `submit()`, and the resume path (`fromMultiplayerSave`)
* deliberately does not go through `submit()`. So replaying a saved game emits no steps and burns
* no sequence numbers. A test in the game pins that, because it is a property rather than an
* accident.
*/ */
up: async ({ effects }) => {}, up: async ({ effects }) => {},
down: IMPOSSIBLE, down: IMPOSSIBLE,