File size: 8,927 Bytes
a14579a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/* ============================================================
   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 }
 */