SpaceCities / net /commandShapes.js
Almaatla's picture
T-009 + T-010: the session/transport seam β€” server/session.js
a14579a unverified
Raw History Blame Contribute Delete
8.93 kB
/* ============================================================
Central JSDoc type definitions for the command wire shape β€” the format a
player action takes between a client and a session, in EVERY mode this game
runs in: single-player over the in-process loopback transport (Phase 1),
and eventually multiplayer over a real socket (Phase 3).
This file has NO runtime code, exactly like engine/types.js, and for the
same reason: it exists purely so `// @ts-check` files (and any editor with
the bundled TypeScript language service) can check against a shared,
accurate model instead of an untyped `any` bag. Because it declares no
imports/exports it is a *script*, so these @typedefs are GLOBAL β€” every
file in the project can refer to `WireCommand`, `Ids`, … by name with no
import, exactly like the engine/ shapes.
WHY THIS SHAPE, THIS EARLY. ADR-0004's decision is "same command encoding"
for single-player and multiplayer β€” not two formats that converge later.
So the wire shape is designed once, in docs/analysis/02-command-wire-protocol.md
Β§3, and adopted here VERBATIM for the commands ordinary skirmish play needs
β€” even though nothing on the loopback transport (Phase 1) actually needs a
compact/serializable format yet. Phase 2's net/commandCodec.js layers
ownership/fog/rate-limit VALIDATION on top of exactly this shape; it does
not redesign it. Getting the shape right now is what makes that additive
rather than a rewrite.
SCOPE. This union covers ordinary skirmish play β€” every command
`inputCommands.js`/`input.js`/`hudSelection.js` issue for a 1v1 (soon N-seat)
match: unit orders (engine/commands.js's full issue* surface) plus
production and research (engine/production.js, engine/techtree.js), which
are core skirmish mechanics, not Odyssey extras.
DELIBERATELY EXCLUDED, for now. Odyssey-only mutators β€” engine/galaxy.js
(jump/lanes/Spaceport/capital ships), engine/colonyPolicy.js,
engine/colony.js (deploy/pack), engine/market.js, engine/diplomacy.js.
Odyssey is explicitly out of the multiplayer story (PRD Β§4 non-goals) and
stays on its current direct-engine-call path; porting it onto this seam,
if ever wanted, is separate follow-on work (TASKS.md T-012a), not a gap in
this file.
============================================================ */
"use strict";
/** engine/formation.js FORMATION_SHAPES / LEADER_POSITIONS β€” the only legal values. */
/** @typedef {"grid"|"line"|"wedge"|"circle"} Shape */
/** @typedef {"front"|"back"|"center"} LeadPos */
/**
* Rides on move / attackMove. Maps to engine/commands.js's `formation` opts bag.
* originX/originY are NOT here β€” issueHoldFormation derives them from the live
* centroid server-side (commands.js:377-380).
* @typedef {Object} WireFormation
* @property {Shape} [s] - shape; default "grid"
* @property {LeadPos} [l] - leaderPos; default "front"
* @property {number} [hx] - headingX, the right-click-drag vector (world-space, not normalized)
* @property {number} [hy] - headingY
*/
/** Ordered, de-duplicated entity ids. ORDER IS LOAD-BEARING β€” see engine/commands.js:105,
* 145, 195, 363: ids[0] is the formation leader, and issueEscort derives ring slots from
* array position. Never sort, canonicalise, or reorder. 1..400 entries.
* @typedef {string[]} Ids */
/**
* @typedef {Object} MoveCommand
* @property {"move"} t
* @property {Ids} ids
* @property {number} x
* @property {number} y
* @property {boolean} [q] - queue (Ctrl-modifier); appends as a waypoint instead of replacing
* @property {WireFormation} [f]
*/
/** @typedef {Object} AttackMoveCommand
* @property {"attackMove"} t @property {Ids} ids @property {number} x @property {number} y
* @property {boolean} [q] @property {WireFormation} [f] */
/** @typedef {Object} HoldFormationCommand
* @property {"holdFormation"} t @property {Ids} ids @property {Shape} [s] @property {LeadPos} [l] */
/** @typedef {Object} PatrolCommand
* @property {"patrol"} t @property {Ids} ids
* @property {Array<{x: number, y: number}>} pts - 1..32 points */
/** @typedef {Object} StopCommand @property {"stop"} t @property {Ids} ids */
/** @typedef {Object} HoldCommand @property {"hold"} t @property {Ids} ids */
/** @typedef {Object} ScoutCommand @property {"scout"} t @property {Ids} ids */
/** @typedef {Object} AttackCommand
* @property {"attack"} t @property {Ids} ids @property {string} target @property {boolean} [q] */
/** @typedef {Object} EscortCommand
* @property {"escort"} t @property {Ids} ids @property {string} target @property {boolean} [q] */
/** @typedef {Object} RepairCommand
* @property {"repair"} t @property {Ids} ids @property {string} target @property {boolean} [q] */
/** @typedef {Object} GatherCommand
* @property {"gather"} t @property {Ids} ids @property {string} node @property {boolean} [q] */
/** @typedef {Object} ServiceCommand - assign workers to service a building's logistics
* @property {"service"} t @property {Ids} ids @property {string} target @property {boolean} [q] */
/** @typedef {Object} FerryCommand - assign workers to ferry a friendly freighter
* @property {"ferry"} t @property {Ids} ids @property {string} target @property {boolean} [q] */
/** @typedef {Object} SetHomeBaseCommand
* @property {"setHomeBase"} t @property {Ids} ids @property {string} target */
/** @typedef {Object} AssistBuildCommand - no buildingType: the session reads it off the resolved site
* @property {"assistBuild"} t @property {Ids} ids @property {string} target @property {boolean} [q] */
/** @typedef {Object} BuildCommand
* @property {"build"} t @property {string} worker @property {string} b @property {number} x @property {number} y */
/** @typedef {Object} RecycleCommand - units AND buildings
* @property {"recycle"} t @property {Ids} ids */
/** @typedef {Object} CancelRecycleCommand
* @property {"cancelRecycle"} t @property {Ids} ids */
/** @typedef {Object} SetAILogisticsCommand
* @property {"setAILogistics"} t @property {Ids} ids @property {boolean} on */
/** @typedef {Object} SetCollectPointCommand
* @property {"setCollectPoint"} t @property {Ids} ids @property {boolean} on */
/** @typedef {"high"|"normal"|"low"} LogiPriority - engine/haul.js LOGI_PRIORITIES */
/** @typedef {Object} SetLogiPriorityCommand
* @property {"setLogiPriority"} t @property {string} building @property {LogiPriority} p */
/** @typedef {Object} SetRallyCommand
* @property {"setRally"} t @property {string} building @property {number} x @property {number} y
* @property {string|null} [node] */
/** Skirmish-essential economy: every match builds units and researches doctrines.
* @typedef {Object} QueueProductionCommand
* @property {"queueProduction"} t @property {string} building @property {string} u
* @property {boolean} [alt] - engine/production.js's alt-recipe flag */
/** @typedef {Object} CancelProductionCommand
* @property {"cancelProduction"} t @property {string} building @property {number} i - queue index */
/** @typedef {Object} ResearchUpgradeCommand - a Refinery doctrine tier (engine/production.js)
* @property {"researchUpgrade"} t @property {string} building @property {string} up */
/** @typedef {Object} ResearchTechCommand - a Foundry/Datacenter tech (engine/techtree.js)
* @property {"researchTech"} t @property {string} building @property {string} tech */
/** @typedef {Object} CancelResearchCommand
* @property {"cancelResearch"} t @property {string} building @property {number} i */
/** @typedef {Object} LightFuseCommand - arm a Helium Bomb (engine/bomb.js)
* @property {"lightFuse"} t @property {string} unit */
/** @typedef {MoveCommand|AttackMoveCommand|HoldFormationCommand|PatrolCommand|StopCommand
* |HoldCommand|ScoutCommand|AttackCommand|EscortCommand|RepairCommand|GatherCommand
* |ServiceCommand|FerryCommand|SetHomeBaseCommand|AssistBuildCommand|BuildCommand
* |RecycleCommand|CancelRecycleCommand|SetAILogisticsCommand|SetCollectPointCommand
* |SetLogiPriorityCommand|SetRallyCommand|QueueProductionCommand|CancelProductionCommand
* |ResearchUpgradeCommand|ResearchTechCommand|CancelResearchCommand|LightFuseCommand
* |BatchCommand} WireCommand */
/** Deliberately NOT nested β€” Phase 2's codec rejects a batch inside a batch.
* 1..16 members, applied in array order, at one tick.
* @typedef {Object} BatchCommand
* @property {"batch"} t @property {WireCommand[]} c */
/**
* What a session hands back after applying a command β€” the loopback transport's
* synchronous return value in Phase 1; a "commandResult" event over the wire
* once a real socket exists (Phase 3).
* @typedef {Object} CommandResult
* @property {boolean} ok
* @property {string} [code] - a REJECT.* reason when ok is false (see net/commandCodec.js, Phase 2)
* @property {Object} [result] - an echo for build-like commands, e.g. { buildingId }
*/