dredless 0.7.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +22 -58
  2. package/dist/browser/dredless.global.js +794 -98
  3. package/dist/browser/dredless.global.js.map +4 -4
  4. package/docs/ACCOUNT.md +146 -0
  5. package/docs/API_REFERENCE.md +63 -2563
  6. package/docs/BROWSER_API.md +151 -70
  7. package/docs/CLIENT.md +183 -0
  8. package/docs/EVENTS.md +136 -0
  9. package/docs/GETTING_STARTED.md +135 -0
  10. package/docs/MACHINES.md +351 -0
  11. package/docs/MANAGEMENT.md +285 -0
  12. package/docs/NODE_API.md +120 -318
  13. package/docs/PLAYER_INVENTORY.md +235 -0
  14. package/docs/PROTOCOL.md +227 -0
  15. package/docs/README.md +44 -0
  16. package/docs/TROUBLESHOOTING.md +91 -0
  17. package/docs/WORLD_STATE.md +183 -0
  18. package/package.json +1 -2
  19. package/src/browser.d.ts +31 -1
  20. package/src/browser.js +10 -0
  21. package/src/client/core.js +302 -15
  22. package/src/client/domains/index.js +57 -7
  23. package/src/client/index.d.ts +1 -1
  24. package/src/index.d.ts +299 -13
  25. package/src/index.js +10 -0
  26. package/src/network/account.js +182 -0
  27. package/src/network/disconnect.d.ts +2 -0
  28. package/src/network/disconnect.js +53 -0
  29. package/src/network/index.d.ts +1 -0
  30. package/src/network/index.js +1 -0
  31. package/src/network/invites.d.ts +0 -3
  32. package/src/network/invites.js +3 -40
  33. package/src/network/session.d.ts +2 -0
  34. package/src/network/session.js +21 -0
  35. package/src/protocol/outbound/ship-management.d.ts +66 -1
  36. package/src/protocol/outbound/ship-management.js +83 -0
  37. package/src/state/model/decoder.js +70 -10
  38. package/src/state/model/index.d.ts +4 -0
  39. package/src/state/model/state.js +22 -7
  40. package/src/state/world/index.js +27 -1
@@ -0,0 +1,235 @@
1
+ # Player input and inventory
2
+
3
+ [Documentation index](README.md) · [Machines](MACHINES.md) · [API reference](API_REFERENCE.md)
4
+
5
+ ## Contents
6
+
7
+ - [Read the player](#read-the-player)
8
+ - [Send input](#send-input)
9
+ - [Persistent input settings](#persistent-input-settings)
10
+ - [Read inventory](#read-inventory)
11
+ - [Move and equip items](#move-and-equip-items)
12
+ - [Blueprints](#blueprints)
13
+ - [Chat and comms](#chat-and-comms)
14
+
15
+ ## Read the player
16
+
17
+ ```ts
18
+ client.player.current(): PlayerSummary | null;
19
+ client.player.entity(): EntityHandle | null;
20
+ client.player.name(): string | null;
21
+ client.player.rank(): CurrentPlayerRank;
22
+ ```
23
+
24
+ `rank()` returns `{shipRank, subrank, isCaptain, patronTier}`. `shipRank` uses names such as `"guest"`, `"crew"`, or
25
+ `"captain"`; it can be `null` before local-player state is known. A WebSocket session ID (`sid`) is not the player's
26
+ entity ID. Management's `refId` is another identifier again.
27
+
28
+ Player summaries from `client.player.current()` and `world.players.all()` include:
29
+
30
+ | Field | Meaning |
31
+ | --- | --- |
32
+ | `heldItemId`, `heldItemName` | Held item, or `null` when empty. |
33
+ | `heldItemAngle` | Held tool angle in radians in the current world's coordinate frame; decoded as the server value divided by `200`. |
34
+ | `heldItemBeamDistance` | Beam distance shared by repair tools and handheld pushers, in world units; decoded as the server value divided by `10`. |
35
+ | `itemMode` | Server-reported numeric item mode code. |
36
+ | `buildMultiplier` | Build reach multiplier, decoded as the server value divided by `10`. |
37
+ | `isLocal` | Whether this entity matches the decrypted camera-focus message. False until that identity is known. |
38
+ | `zoomedOut` | Server-requested camera zoom state. This flag alone does not identify which machine the player is operating. |
39
+ | `invisible` | Server invisibility flag; the official client hides the sprite and excludes it from name suggestions for other players. |
40
+ | `cheats` | `{noclip, sandboxBuild}`: noclip/flight and sandbox build mode flags. |
41
+ | `itemEffectActive` | Shared item effect flag. The official renderer uses it for rocket/hover pack exhaust and certain held-item effects. Repair/pusher beams also depend on a positive `heldItemBeamDistance`. |
42
+ | `animation` | `{facing, orientation, walking}`: sprite mirror sign, numeric orientation code, and speed-based limb animation flag. |
43
+ | `appearance` | Base outfit colors, cosmetic styles, and equipped item visual overrides; `null` until the appearance component is available. |
44
+
45
+ `itemMode`, `heldItemAngle`, and `heldItemBeamDistance` are `null` until available. `buildMultiplier` defaults to `1`, matching the official
46
+ client. `heldItemAngle` replaces `repairTargetAngle`, whose previous scaling
47
+ was incorrect, and `zoomedOut` replaces `piloting`. Helm `occupied` remains inferred from zoom state combined with
48
+ helm updates. `heldItemBeamDistance` replaces `repairTargetDistance`, and `itemEffectActive` replaces `toolEffectActive`.
49
+ Other summary fields include name, ranks, patron status, mute status, and action
50
+ preview; see [PlayerSummary](../src/index.d.ts) for the complete shape.
51
+
52
+ `appearance` contains packed RGB `bodyColor`, `legsColor`, `skinColor`, and `feetColor`, plus `hair`, `hat`, `face1`,
53
+ and `face2`, each shaped as `{color, styleId}`. Style IDs index the official client's cosmetic catalogs; `0` means no
54
+ style. Outfit fields default to zero when omitted from the initial appearance component, and sparse updates retain
55
+ previous values. The style catalog's image paths and rendering rules are not bundled with these summaries.
56
+
57
+ `appearance.itemOverrides` contains `back`, `hands`, `feet`, `head1`, and `head2`. Each is `null` or an
58
+ `{itemId, itemName}` entry describing an equipped item visual. These are separate from the base cosmetic
59
+ styles. `back` includes backpacks, rocket packs, and hover packs; it replaces the incorrectly named `body` override.
60
+ The inventory still has separate body and back slots. The replicated appearance component has no confirmed body
61
+ item visual field. The raw appearance component is retained in `appearance.state`.
62
+
63
+ For handheld pushers, `itemMode & 1` selects the beam direction. Packs can be equipped using
64
+ `client.inventory.equip(source, "back")`; generic player input controls their use.
65
+
66
+ `animation.facing` is normally `-1` or `1`; `orientation` retains the server's numeric code. `animation.walking`
67
+ selects the official renderer's speed-based limb swing rather than its hovering pose. It can be enabled while the
68
+ player stands still, so it should not be used to detect movement. Raw fields whose meaning is not confirmed remain
69
+ in `player.state`.
70
+
71
+ Local identity comes from packet `21`, not a player animation flag or the WebSocket session ID. When replaying
72
+ captures, pass camera-focus packets to `WorldStore.apply()` as well as world metadata and model packets. Standalone
73
+ `ModelState` consumers can set the identity with `model.setLocalPlayerEntity(entityId)` and clear it with `null`.
74
+ When attaching after the camera-focus packet has already been received, local identity remains unknown until another
75
+ focus packet arrives or the entity is set explicitly on the world's model.
76
+
77
+ ## Send input
78
+
79
+ These methods return the client after sending or queueing input, without waiting for action success:
80
+
81
+ ```ts
82
+ client.player.move(vector?: {x?: number; y?: number}, command?: Command): DredlessClient;
83
+ client.player.aim(point?: {x?: number; y?: number; mx?: number; my?: number}, command?: Command): DredlessClient;
84
+ client.player.action(flags?: Command, command?: Command): DredlessClient;
85
+ client.player.useEntity(entity: EntityRef, options?: {invSlot?: number; hold?: boolean},
86
+ command?: Command): DredlessClient;
87
+ client.player.useHeldItem(options?: {invSlot?: number; hold?: boolean}, command?: Command): DredlessClient;
88
+ client.player.placeHeldItem(options?: {invSlot?: number; hold?: boolean}, command?: Command): DredlessClient;
89
+ client.player.rotateHeldItem(options?: {invSlot?: number; hold?: boolean}, command?: Command): DredlessClient;
90
+ client.player.selectSlot(invSlot?: number, command?: Command): DredlessClient;
91
+ ```
92
+
93
+ Movement `x`/`y` are input axes; aiming supplies game-coordinate `mx`/`my` (`x`/`y` are accepted by the aim helper).
94
+ `invSlot` defaults to `0` and `hold` to `true` for use/rotate helpers. These calls do not implement pathfinding, range
95
+ checks, or an automatic input loop.
96
+
97
+ Each send builds a complete command: unspecified command fields are defaulted, not inherited from the previous action.
98
+ Persistent settings below are the exception. If you need movement and aim/action in the same input, supply the other
99
+ fields through `command` instead of assuming separate sends merge.
100
+
101
+ **Fragment:** send combined input, then later send an explicit stop:
102
+
103
+ ```js
104
+ client.player.move({x: 1, y: 0}, {mx: 10, my: 5});
105
+ // When the application decides movement should stop:
106
+ client.player.move({x: 0, y: 0});
107
+ ```
108
+
109
+ `Command` exposes input flags (`jump`, `drop`, `act1`, `act2`, held/alternate variants), coordinates, inventory slot,
110
+ entity focus, menu selection, view settings, and drag payloads. Use [the declared Command shape](../src/index.d.ts) for
111
+ the full list; signed packet construction is described in [protocol tools](PROTOCOL.md#commands-and-payloads).
112
+
113
+ ## Persistent input settings
114
+
115
+ ```ts
116
+ client.player.inputSettings(): CurrentInputSettings;
117
+ client.player.setInputSettings(settings?: InputSettings, options?: {send?: boolean}): DredlessClient;
118
+ client.player.setView(width: number, height: number, options?: {send?: boolean}): DredlessClient;
119
+ client.player.setScreenSize(width: number, height: number, options?: {send?: boolean}): DredlessClient;
120
+ client.player.setWrenchMode(mode: WrenchMode, options?: {send?: boolean}): DredlessClient;
121
+ client.player.setTurretMode(mode: TurretMode, options?: {send?: boolean}): DredlessClient;
122
+ ```
123
+
124
+ `InputSettings` accepts `wrenchMode`, `turretMode`, `viewWidth`, `viewHeight`, `screenWidth`, and `screenHeight`. View
125
+ sizes must be positive finite numbers; screen sizes must be positive integers. `send` defaults to `true`; `false` only
126
+ updates defaults for later commands.
127
+
128
+ | Constant | Values |
129
+ |--------------|----------------------------------------------------|
130
+ | `WrenchMode` | `DropAllItems`, `GrabPrimaryItems`, `GrabAllItems` |
131
+ | `TurretMode` | `ContinuousFire`, `VolleyFire` |
132
+
133
+ The constants' values are lowercase hyphenated names such as `"grab-primary-items"`. Defaults are grab-primary-items and
134
+ continuous-fire. These settings persist across input sends; ordinary movement/action fields do not.
135
+
136
+ ## Read inventory
137
+
138
+ ```ts
139
+ client.inventory.state(): InventoryState;
140
+ client.inventory.hotbarSize(): number;
141
+ client.inventory.allSlots(): InventorySlotHandle[];
142
+ client.inventory.hotbarSlots(): InventorySlotHandle[];
143
+ client.inventory.equipmentSlots(): Record<EquipmentSlot, InventorySlotHandle>;
144
+ client.inventory.slot(ref: InventorySlotRef): InventorySlotHandle;
145
+ client.inventory.findItem(itemId: number, options?: {area?: InventoryArea}): InventorySlotHandle | null;
146
+ client.inventory.findItems(itemId: number, options?: {area?: InventoryArea}): InventorySlotHandle[];
147
+ client.inventory.firstEmpty(options?: {area?: InventoryArea}): InventorySlotHandle | null;
148
+
149
+ type InventoryArea = "all" | "hotbar" | "equipment";
150
+ type InventorySlotRef = number | EquipmentSlot | InventorySlotHandle | InventorySlotSnapshot;
151
+ ```
152
+
153
+ The normalized state has `hotbarSize`, `slots`, `hotbar`, and an `equipment` object. Hotbar size comes from the protocol
154
+ and is independent of the fixed equipment indexes.
155
+
156
+ | `EquipmentSlot` key | Value | Absolute slot |
157
+ |---------------------|-----------|---------------|
158
+ | Head | `"head"` | 16 |
159
+ | Face | `"face"` | 17 |
160
+ | Body | `"body"` | 18 |
161
+ | Back | `"back"` | 19 |
162
+ | Hands | `"hands"` | 20 |
163
+ | Feet | `"feet"` | 21 |
164
+
165
+ A slot handle exposes `index`, `kind`, `equipmentSlot`, `itemId`, `itemName`, `count`, `empty`, `exists()`, and
166
+ `snapshot()`. A slot snapshot is a frozen copy or `null`. Slot handles are live reads; no item is guaranteed to be
167
+ present at index zero.
168
+
169
+ ## Move and equip items
170
+
171
+ ```ts
172
+ client.inventory.move(source: InventorySlotRef, target: InventorySlotRef,
173
+ options?: {split?: boolean}, command?: Command): DredlessClient;
174
+ client.inventory.equip(source: InventorySlotRef, equipmentSlot?: EquipmentSlot,
175
+ options?: {split?: boolean}, command?: Command): DredlessClient;
176
+ client.inventory.unequip(equipmentSlot: EquipmentSlot, target?: InventorySlotRef,
177
+ options?: {split?: boolean}, command?: Command): DredlessClient;
178
+ client.inventory.select(slot: InventorySlotRef, command?: Command): DredlessClient;
179
+
180
+ slot.moveTo(target: InventorySlotRef, options?: {split?: boolean}, command?: Command): DredlessClient;
181
+ slot.equip(equipmentSlot?: EquipmentSlot, options?: {split?: boolean}, command?: Command): DredlessClient;
182
+ slot.unequip(target?: InventorySlotRef, options?: {split?: boolean}, command?: Command): DredlessClient;
183
+ slot.select(command?: Command): DredlessClient;
184
+ ```
185
+
186
+ These are the ordinary call forms; option-object overloads are in the [declarations](../src/index.d.ts). Omit
187
+ `equipmentSlot` to infer it from the source item schema. Inference throws for an empty slot or an item not classified as
188
+ equipment. Unequip targets default to slot `0`; check that destination before moving items.
189
+
190
+ All actions send input and return immediately. Observe `inventory` for the resulting state. A server can reject the move
191
+ even when local argument validation succeeds.
192
+
193
+ **Fragment:** `equipmentItemId` must be an actual equipment item ID your application selected:
194
+
195
+ ```js
196
+ const source = client.inventory.findItem(equipmentItemId, {area: "hotbar"});
197
+ if (!source) throw new Error("Requested item is not in the hotbar");
198
+ source.equip();
199
+ ```
200
+
201
+ ## Blueprints
202
+
203
+ ```ts
204
+ interface BlueprintPlacement {
205
+ x: number;
206
+ y: number;
207
+ width: number;
208
+ height: number;
209
+ source: string;
210
+ }
211
+
212
+ client.player.placeBlueprint(placement: BlueprintPlacement,
213
+ options?: {invSlot?: number; hold?: boolean; mx?: number; my?: number},
214
+ command?: Command): DredlessClient;
215
+ ```
216
+
217
+ Supply a real blueprint source string and matching dimensions. `x`/`y` must be finite and width/height positive
218
+ integers; runtime builders also accept protocol aliases `w`/`h`. The helper sends the blueprint placement message and
219
+ uses the selected inventory slot, aiming at the placement coordinates unless overridden. The inventory item, range,
220
+ permissions, and game rules must allow placement. There is no placement-success promise.
221
+
222
+ `buildBlueprintPlacementMessage()` only builds the message; `client.net.sendBlueprintPlacement()` sends it without the
223
+ player-use step. Blueprint preview state is available as an entity feature; it is not proof a placement completed.
224
+
225
+ ## Chat and comms
226
+
227
+ ```ts
228
+ client.sendChatMessage(message?: string): DredlessClient;
229
+ client.sendCommsMessage(message?: string): DredlessClient;
230
+ commsStation.sendMessage(message?: string): DredlessClient;
231
+ ```
232
+
233
+ Chat uses a chat protocol message. Comms targets the active comms interaction; a station handle's send method does not
234
+ include its ID in that message. Establish the interaction first and observe `comms`/`chat` events. Formatting and
235
+ outbound builders are listed in [protocol tools](PROTOCOL.md).
@@ -0,0 +1,227 @@
1
+ # Protocol tools and debugging
2
+
3
+ [Documentation index](README.md) · [Client networking](CLIENT.md#send-commands-and-messages) · [API reference](API_REFERENCE.md)
4
+
5
+ ## Contents
6
+
7
+ - [Import map](#import-map)
8
+ - [Commands and payloads](#commands-and-payloads)
9
+ - [Management builders](#management-builders)
10
+ - [Configuration builders](#configuration-builders)
11
+ - [Generator maze helpers](#generator-maze-helpers)
12
+ - [HTTP and invite tools](#http-and-invite-tools)
13
+ - [Debug domains and state classes](#debug-domains-and-state-classes)
14
+ - [Decoding limitations](#decoding-limitations)
15
+
16
+ ## Import map
17
+
18
+ Prefer high-level domains when implementing game actions. These tools are for integrations, payload construction, capture analysis, and offline decoding. Check the subpath's declarations for full overloads and constants.
19
+
20
+ | Import | Main exports | Declaration |
21
+ |---------------------------------------------------------------------------------|------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
22
+ | `dredless/protocol` | Frame/world decoders and shared outbound builders | [protocol/index.d.ts](../src/protocol/index.d.ts) |
23
+ | `dredless/protocol/msgpack` | `encodeMsgpack`, `decodeMsgpack` | [codec/msgpack.d.ts](../src/protocol/codec/msgpack.d.ts) |
24
+ | `dredless/protocol/commands` | `buildSignedCommandPacket` | [commands.d.ts](../src/protocol/outbound/commands.d.ts) |
25
+ | `dredless/protocol/blueprint` | Blueprint placement builder/type constant | [blueprint.d.ts](../src/protocol/outbound/blueprint.d.ts) |
26
+ | `dredless/protocol/comms` | Comms builder, rich-text flattening/normalization | [comms.d.ts](../src/protocol/outbound/comms.d.ts) |
27
+ | `dredless/protocol/inventory` | Drag/equip builders and slot normalization | [inventory.d.ts](../src/protocol/outbound/inventory.d.ts) |
28
+ | `dredless/protocol/ship-management` | Management builders, normalizers, action constants | [ship-management.d.ts](../src/protocol/outbound/ship-management.d.ts) |
29
+ | `dredless/protocol/sign` | Sign builder/display-mode helpers | [sign.d.ts](../src/protocol/outbound/sign.d.ts) |
30
+ | `dredless/protocol/ui-config` | Binary config/clipboard builders and enums | [ui-config.d.ts](../src/protocol/outbound/ui-config.d.ts) |
31
+ | `dredless/game/generator-maze` | Maze generation/solution helpers | [generator-maze.d.ts](../src/state/generator-maze.d.ts) |
32
+ | `dredless/crypto/chacha` | `decryptPayload` | [chacha.d.ts](../src/crypto/chacha.d.ts) |
33
+ | `dredless/compression/lz4` | `decompressLz4Frame` | [lz4.d.ts](../src/compression/lz4.d.ts) |
34
+ | `dredless/model`, `dredless/game/model` | `ModelState`, `decodeModelData` | [model/index.d.ts](../src/state/model/index.d.ts) |
35
+ | `dredless/world`, `dredless/state` | World/model state classes | [world/index.d.ts](../src/state/world/index.d.ts), [state/index.d.ts](../src/state/index.d.ts) |
36
+ | `dredless/network` | Sessions, connections, discovery, ship/invite reads, disconnects | [network/index.d.ts](../src/network/index.d.ts) |
37
+ | `dredless/servers`, `dredless/ships`, `dredless/connection`, `dredless/session` | Focused network APIs | [package export map](../package.json) |
38
+
39
+ Runtime entrypoints re-export many shared builders, but not every low-level decoder or action constant. In particular, `decodeIncomingFrame()` belongs to `dredless/protocol`, not the root namespace. Importing from `node_modules/dredless/src/...` is unnecessary and bypasses the supported export map.
40
+
41
+ ## Commands and payloads
42
+
43
+ ```ts
44
+ encodeMsgpack(value: unknown): Uint8Array;
45
+ decodeMsgpack(bytes: Uint8Array | ArrayBuffer | number[]): unknown;
46
+ decodeIncomingFrame(data: string | Uint8Array | ArrayBuffer | number[]): unknown;
47
+ buildSignedCommandPacket(command: Command, sessionId: number): Uint8Array;
48
+ buildBlueprintPlacementMessage(placement: BlueprintPlacement): BlueprintPlacementMessage;
49
+ buildChatMessage(message?: string): ChatMessage;
50
+ buildCommsMessage(message?: string): CommsMessage;
51
+ buildSignTextMessage(text?: string, mode?: SignDisplayMode): SignTextMessage;
52
+ flattenRichText(value: unknown): string;
53
+ normalizeCommsEvent(event: unknown): CommsEvent;
54
+ ```
55
+
56
+ `decodeIncomingFrame()` parses text as JSON and binary as MessagePack; it does not apply decoded packets to state. A model-bearing packet can still contain encrypted/compressed inner payloads.
57
+
58
+ `buildSignedCommandPacket()` returns the complete signed binary input frame. Ordinary message builders return decoded objects. UI config builders below return inner byte payloads. Keep these three representations distinct: `sendMessage()`/`sendRaw()` encode objects, while `send()` signs input and `sendUiConfig()` wraps a config payload.
59
+
60
+ ```ts
61
+ buildInventoryDragCommand(source: number, target: number, split?: boolean): InventoryDragCommand;
62
+ buildEquipItemCommand(source: number, slot: EquipmentSlot, split?: boolean): InventoryDragCommand;
63
+ buildUnequipItemCommand(slot: EquipmentSlot, target?: number, split?: boolean): InventoryDragCommand;
64
+ normalizeEquipmentSlot(slot: EquipmentSlot): 16 | 17 | 18 | 19 | 20 | 21;
65
+ equipmentSlotName(slot: number): EquipmentSlot | null;
66
+ normalizeInventoryEvent(event: unknown): InventoryState;
67
+ normalizeSignDisplayMode(mode?: SignDisplayMode): 0 | 1 | 2;
68
+ signDisplayModeName(mode: number): SignDisplayMode | null;
69
+ ```
70
+
71
+ Drag builders produce command fields to pass to input sending, not independent game messages. High-level [inventory methods](PLAYER_INVENTORY.md#move-and-equip-items) also accept slot handles and infer equipment locations.
72
+
73
+ ## Management builders
74
+
75
+ These return `ShipManagementMessage` objects. They perform local argument validation but no I/O or response waiting:
76
+
77
+ ```ts
78
+ buildShipManagementMessage(act: string, arg?: unknown, extra?: Record<string, unknown> | null): ShipManagementMessage;
79
+ buildShipPrivacyMessage(privacy: ShipPrivacy): ShipManagementMessage;
80
+ buildShipGravityMessage(direction: ShipGravityDirection): ShipManagementMessage;
81
+ buildSetPatronPerksMessage(enabled: boolean): ShipManagementMessage;
82
+ buildPlayerListMessage(): ShipManagementMessage;
83
+ buildInviteResetMessage(): ShipManagementMessage;
84
+ buildSaveShipMessage(): ShipManagementMessage;
85
+ buildShipRenameRequestMessage(): ShipManagementMessage;
86
+ buildShipRenameCommitMessage(name: string, color: string): ShipManagementMessage;
87
+ buildStarterRecoveryMessage(itemId: number): ShipManagementMessage;
88
+ buildSetPlayerRankMessage(refId: number, rank: ShipPlayerRank): ShipManagementMessage;
89
+ buildKickPlayerMessage(refId: number): ShipManagementMessage;
90
+ buildBanPlayerMessage(refId: number): ShipManagementMessage;
91
+ buildUnbanPlayerMessage(refId: number): ShipManagementMessage;
92
+ buildDemoteSelfMessage(): ShipManagementMessage;
93
+ ```
94
+
95
+ `ShipManagementMessage` has `{type: 4, act, arg}` plus optional action-specific fields such as `rank` and `arg2`. The generic builder does not implement the action-specific permission/confirmation flows. Prefer [management methods](MANAGEMENT.md) for save, rename, invite reset, demotion, and recovery.
96
+
97
+ Normalization helpers:
98
+
99
+ ```ts
100
+ normalizePrivacy(value: ShipPrivacy): 0 | 1;
101
+ normalizePlayerRank(value: ShipPlayerRank): 0 | 1 | 3;
102
+ normalizeGravityDirection(value: ShipGravityDirection): 0 | 1 | 2 | 3;
103
+ normalizeShipConfig(event: unknown): ShipConfig;
104
+ normalizeCaptainSubrank(event: unknown): CaptainSubrank;
105
+ normalizeShipPlayerList(event: unknown, previous?: ShipPlayerList | null,
106
+ captain?: CaptainSubrank | null): ShipPlayerList;
107
+ normalizeCrewLog(event: unknown, previous?: CrewLog | null): CrewLog;
108
+ ```
109
+
110
+ Pass the inner session submessage, not the outer `{type: 25, submessage}` packet. Player-list/log normalizers merge with the previous normalized state; omitting it begins a fresh state. [Management](MANAGEMENT.md) describes their reset/amend/remove semantics. Action-name constants, `SHIP_MANAGEMENT_TYPE`, and `CREW_LOG_LENGTH` are exported by the management subpath.
111
+
112
+ ## Configuration builders
113
+
114
+ All return `Uint8Array` payloads. Their configuration types and operational prerequisites are covered in [machines](MACHINES.md); these low-level functions do not merge cached state or open an interaction.
115
+
116
+ | Family | Builder calls |
117
+ |------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
118
+ | Loader | `buildLoaderConfigData(entity, config?)`, `buildLoaderFullConfigData(entity, config?)`, `buildLoaderFilterConfigData(entity, mode?)`, `buildLoaderFilterItemsData(entity, slots?)` |
119
+ | Pusher | `buildPusherConfigData(entity, config?)`, `buildPusherFilterItemsData(entity, slots?)` |
120
+ | Navigation | `buildNavigationUnitConfigData(entity, config)`, `buildNavigationUnitPasteConfigData(entity, config)` |
121
+ | Hatch | `buildCargoHatchFilterConfigData(entity, mode?)`, `buildCargoHatchFilterItemsData(entity, slots?)`, `buildCargoHatchFullConfigData(entity, config?)` |
122
+ | Ejector | `buildCargoEjectorDirectionData(entity, direction)`, `buildCargoEjectorPasteConfigData(entity, direction)` |
123
+ | Generator puzzle | `buildGeneratorMazePuzzleData(entity, solution)` |
124
+
125
+ Clipboard payload builders:
126
+
127
+ - `buildLoaderClipboardConfigData(config?)`, `buildLoaderCopyConfigData(config?)`
128
+ - `buildNavigationUnitClipboardConfigData(config)`
129
+ - `buildCargoHatchCopyConfigData(config?)`
130
+ - `buildCargoEjectorCopyConfigData(direction)`, `buildCargoEjectorClipboardDirectionData(direction)`
131
+ - `buildGeneratorClipboardDirectionData(direction)`
132
+ - `buildExpandoClipboardAngleData(degrees)`
133
+ - `buildClipboardConfigData(target, commandName, values?)`
134
+ - `buildClipboardFixedAngleData(target, direction)`
135
+
136
+ Navigation builder config requires `destination`; hatch full/copy config contains `filterMode` and `filterSlots`. Generic clipboard values are `Iterable<number>`; `target` is a `ClipboardTarget` name or numeric selector. Full exact signatures and target aliases are maintained in [ui-config.d.ts](../src/protocol/outbound/ui-config.d.ts).
137
+
138
+ **Fragment:** `loader` is a live loader handle and `client` owns it. A complete explicit configuration avoids the current numeric-default enum limitation:
139
+
140
+ ```js
141
+ import {buildLoaderConfigData} from "dredless/protocol/ui-config";
142
+
143
+ const payload = buildLoaderConfigData(loader.id, {
144
+ pick: "middle-left", place: "middle-right", priority: "normal", cycle: 5, stack: 16
145
+ });
146
+ client.net.sendUiConfig(payload);
147
+ ```
148
+
149
+ Named-enum validation and the numeric/default mismatch also affect low-level optional-config calls. Supply all required enum fields explicitly as described in [loaders](MACHINES.md#loaders), [pushers](MACHINES.md#pushers), and [hatches](MACHINES.md#hatches-ejectors-and-clipboard-settings).
150
+
151
+ ## Generator maze helpers
152
+
153
+ ```ts
154
+ generateGeneratorMaze(seed: number): GeneratorMaze;
155
+ solveGeneratorMazeSeed(seed: number): string;
156
+ maybeSolveGeneratorMazeSeed(seed: number | null | undefined): string | null;
157
+ ```
158
+
159
+ Generation returns `{seed, width, height, cells, rows, solution}`. Each cell records coordinates, encoded value/hex/digit, wall flags, backtrack direction, and marker. Solving computes the solution string without network activity; the `maybe` form returns `null` for unusable input instead of requiring a known seed. Use a seed actually decoded for the target generator. To send a solution, call the generator handle's `solvePuzzle()`.
160
+
161
+ ## HTTP and invite tools
162
+
163
+ Session/discovery signatures are in the [Node](NODE_API.md) and [browser](BROWSER_API.md) references. Additional named helpers include:
164
+
165
+ ```ts
166
+ fetchInviteMetadata(invite: string | URL, options?: InviteFetchOptions): Promise<InviteMetadata>;
167
+ parseInviteMetadata(html: string): InviteMetadata;
168
+ createShipSpec(name?: string, color?: string): ShipSpec;
169
+ createInviteShipSpec(code: string): ShipSpec;
170
+ ```
171
+
172
+ `InviteFetchOptions` accepts `fetchImpl` for a custom HTTP implementation. Invite metadata contains name, displayed ship ID, preview image URL, hostname, and invite code. Preview images are not fetched or decoded; consumers can process `previewImageUrl` themselves. The parser expects the invite page's metadata, including an invite URL, and may reject unrelated HTML.
173
+
174
+ Ship-spec helpers are available from `dredless/ships`; they construct objects without joining. The network subpath also exports discovery/resolution helpers; consult its re-export declarations when building custom connection flows.
175
+
176
+ ## Debug domains and state classes
177
+
178
+ ```ts
179
+ client.debug.packets(): unknown[];
180
+ client.debug.decodeErrors(): unknown[];
181
+ client.debug.worldStore(): WorldStore;
182
+ client.debug.modelTable(worldId: number, tableId: number): Map<number, ModelRecord>;
183
+ client.debug.modelRecord(worldId: number, tableId: number, entityId: number): ModelRecord | null;
184
+ client.debug.entities(scope?: ReadWorldScope): EntityDebugSummary[];
185
+ client.debug.entity(scope: ReadWorldScope, entity: EntityRef): EntityDebugSummary | null;
186
+ client.debug.puiPanels(): PuiEvent[];
187
+ client.debug.commsPanels(): CommsEvent[];
188
+ ```
189
+
190
+ Debug reads expose retained/raw state, sometimes by reference. Do not mutate it while a live client is decoding. Capture history is bounded by [client retention options](CLIENT.md#construction-and-retention).
191
+
192
+ | Class | Purpose | Core methods |
193
+ |--------------|----------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
194
+ | `WorldStore` | Applies decoded packets into worlds | `apply(packet)`, `get(id)`, `current()`, `shipWorld()`, `overworld()`, `snapshot(options?)` |
195
+ | `WorldState` | Metadata, tiles, events, model for one world | `readMeta(packet)`, `decodeEncrypted(data)`, `entity(id)`, `entities()`, `table(id)`, `record(table, entity)`, `snapshot(options?)` |
196
+ | `ModelState` | ECS records and derived summaries | `apply(bytes)`, `table(id)`, `record(table, entity)`, `entities()`, `machines()`, `transforms()`, `snapshot(options?)`, `tablesSnapshot()` |
197
+
198
+ World snapshots can include tiles/model tables/blocks; model snapshots use `includeTables` and `includeBlocks`. Full constructors, read methods, and structures are in [world declarations](../src/state/world/index.d.ts) and [model declarations](../src/state/model/index.d.ts). Some direct implementation options are broader than their declared overloads.
199
+
200
+ Additional state APIs used in offline tooling:
201
+
202
+ ```ts
203
+ worldStore.ids(): number[];
204
+ worldStore.currentShipEntity(): EntityDebugSummary | null;
205
+ worldState.addCommsBubble(packet: unknown): CommsBubbleSummary;
206
+ worldState.applyTile(value: unknown): Tile | null;
207
+ worldState.applyChunk(value: unknown): Tile[] | null;
208
+ worldState.setTile(tile: Tile): Tile;
209
+ worldState.normalizeTile(tile: Tile): Tile;
210
+ worldState.tileDefinition(material: number): TileDefinition | null;
211
+ modelState.itemHolders(): ItemHolderSummary[];
212
+ modelState.fabricators(): FabricatorSummary[];
213
+ modelState.players(): PlayerSummary[];
214
+ modelState.setLocalPlayerEntity(entity: number | null): void;
215
+ modelState.shipControls(): ShipControlSummary[];
216
+ modelState.shipMetadata(): ShipWorldMetadataSummary | null;
217
+ ```
218
+
219
+ Tile/chunk setters update the local decoder state only; they are not server block-edit commands. `WorldStore.get(id)` creates a world when absent; use `.worlds.get(id)` to inspect without creating one. Read aggregate summaries through domains in ordinary live applications.
220
+
221
+ Binary tools include `decryptPayload(wireBytes, worldId, seed)`, `decompressLz4Frame(bytes)`, and `decodeModelData(bytes)`. The protocol aggregate also exposes inner world/model/chunk decoders. These expect the appropriate layer, seed, and world context; a captured WebSocket frame is not automatically a valid argument to a model decoder.
222
+
223
+ ## Decoding limitations
224
+
225
+ A decoder can retain partial results while recording unsupported tags or malformed sections. Read `decode-error`, model errors, and the retained raw packet before concluding an entity/component is absent. Website parsers and protocol internals can change independently of this package.
226
+
227
+ Raw `ModelRecord` keys (`q20`, blobs, table IDs, and section tags) are implementation details. Prefer normalized public summaries for application code. Protocol research belongs in a local capture workflow; avoid encoding application behavior around guessed raw-field semantics when a typed field exists.
package/docs/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # Documentation
2
+
3
+ Dredless separates HTTP account sessions from live game connections. A `Session` authenticates requests; a `DredlessClient` owns a WebSocket and decoded world state.
4
+ In a browser, `BrowserSession` uses the ambient browser cookie jar.
5
+
6
+ ## Start here
7
+
8
+ 1. [Getting started](GETTING_STARTED.md): read servers, join through an invite, inspect state, and close.
9
+ 2. Choose [Node sessions](NODE_API.md) or [browser setup](BROWSER_API.md).
10
+ 3. Read [completion and lifecycle](CLIENT.md#completion-and-readiness) before sending actions.
11
+
12
+ ## Choose a task
13
+
14
+ | I want to… | Read |
15
+ |------------------------------------------------------------------|----------------------------------------------------------------------------------------|
16
+ | Reuse an account or anonymous identity | [Node authentication](NODE_API.md#authentication-and-reuse) |
17
+ | Join an active ship or load a saved one | [Connection methods](NODE_API.md#join-start-and-invites) |
18
+ | Observe an official-client socket | [Browser attachment](BROWSER_API.md#attach-to-an-existing-socket) |
19
+ | Handle disconnects and retain useful state | [Client lifecycle](CLIENT.md) |
20
+ | Subscribe to model, inventory, or management updates | [Event catalog](EVENTS.md) |
21
+ | Find an entity, machine, or nearby ship | [World and entity reads](WORLD_STATE.md) |
22
+ | Move, aim, equip, or place a blueprint | [Player and inventory](PLAYER_INVENTORY.md) |
23
+ | Use a context menu or set door access | [Machines and context menus](MACHINES.md#context-menus-and-access) |
24
+ | Configure a loader, pusher, launcher, or navigation unit | [Machine reference](MACHINES.md) |
25
+ | Save, rename, reset invites, or manage crew | [Ship management](MANAGEMENT.md) |
26
+ | Read crew logs or apply ship patron perks | [Ship management](MANAGEMENT.md#crew-logs), [patron perks](MANAGEMENT.md#patron-perks) |
27
+ | Read account details, change a clan tag, or remove account perks | [Account methods](ACCOUNT.md) |
28
+ | Decode captures or build protocol messages | [Protocol and debugging](PROTOCOL.md) |
29
+ | Understand an error or missing value | [Troubleshooting](TROUBLESHOOTING.md) |
30
+
31
+ ## Reference conventions
32
+
33
+ [API_REFERENCE.md](API_REFERENCE.md) indexes the reference pages and defines ID, return-value, and type conventions. Each page includes calling signatures and the behavior needed to use them. The package's [TypeScript declarations](../src/index.d.ts) contain complete state shapes and overloads; the [browser declarations](../src/browser.d.ts) describe browser-specific exports.
34
+
35
+ Examples marked **fragment** assume a connected `client` or authenticated `session` from the setup guides. They are not standalone programs. Examples that change game/account state describe their prerequisites before the code.
36
+
37
+ Documentation describes the current checkout. Protocol support and website scraping can change with the upstream game. See [troubleshooting](TROUBLESHOOTING.md) for the specific failure surfaces.
38
+
39
+ ## Contributing to these docs
40
+
41
+ Put setup in the runtime guides, behavior in the relevant domain page, callbacks in the event catalog, and wire-format details in protocol documentation.
42
+ Add navigation links for new tasks. Include calling signatures, prerequisites, units/ranges, return/completion meaning, errors, and one contextual example where they affect usage.
43
+ Keep complete state shapes in the declarations; avoid copying a second exhaustive type catalog into Markdown.
44
+ Put changes between releases in release notes rather than describing the current API as "now" doing something.
@@ -0,0 +1,91 @@
1
+ # Troubleshooting and limitations
2
+
3
+ [Documentation index](README.md) · [Client lifecycle](CLIENT.md) · [Events](EVENTS.md)
4
+
5
+ ## Contents
6
+
7
+ - [Connection and identity](#connection-and-identity)
8
+ - [Missing or stale state](#missing-or-stale-state)
9
+ - [Actions that appear to do nothing](#actions-that-appear-to-do-nothing)
10
+ - [Timeouts and retries](#timeouts-and-retries)
11
+ - [Browser and HTTP problems](#browser-and-http-problems)
12
+ - [Useful diagnostics](#useful-diagnostics)
13
+
14
+ ## Connection and identity
15
+
16
+ | Symptom | Check |
17
+ |------------------------------------------------|------------------------------------------------------------------------------------------------|
18
+ | `fetch is not available` | Run on a Node runtime with built-in global `fetch`, or provide it before calling HTTP APIs |
19
+ | Import/top-level-await syntax error | Use `.mjs`/ES modules in Node; use a module script or async wrapper in browser scripts |
20
+ | A different anonymous account appears each run | Retain/reuse `anonKey`; factories without a key can create a new identity |
21
+ | Top-level join ignores your account | Pass your `session`, or call the method on that session directly |
22
+ | Join rejects with `4007` and `never_load=true` | If the ship is saved, use `startShip()` so the server may load it |
23
+ | Invite join fails | Use the invite code segment and the intended server; check permission/expiry and close details |
24
+ | Connection closes after saving | Expected with `Saved` (4015); successful save ends the connection |
25
+ | Client stays disconnected | There is no automatic reconnect; create a new connection/client |
26
+
27
+ `createSession()` does not perform a registered-provider login. [Node authentication](NODE_API.md#authentication-and-reuse) explains using an existing session cookie; [browser authentication](BROWSER_API.md#execution-context) uses the ambient jar.
28
+
29
+ ## Missing or stale state
30
+
31
+ - A ready client can still have missing world/components/panels. Check nullable reads, then subscribe to the relevant update and read the cache once.
32
+ - A handler installed after an awaited factory can miss initial events. Use a connection descriptor and attach listeners before awaiting readiness when those first events matter.
33
+ - Attachment cannot recover frames received before it began. Inventing `ready`/`sid` metadata does not reconstruct ECS state.
34
+ - Entity IDs are scoped by world. Do not pass an overworld ship entity ID where an interior machine ID is expected.
35
+ - A nearby ship's presence does not imply its interior is loaded. `includeWorld` only includes already-decoded data.
36
+ - Handles and raw summaries have different lifetimes. Entity handles read live state; ship-read handles hold the summary from that query. Re-query ship reads after updates.
37
+ - Disconnect retains last-known worlds while clearing active management/panel state. Do not treat retained data as current server state.
38
+ - Client/world snapshots are not universally deep-cloned or frozen. Mutating returned objects can affect shared cached data; select and clone fields for history.
39
+ - Unsupported decoding can look like absent state. Inspect `decode-error` before assuming the server has no such component.
40
+
41
+ ## Actions that appear to do nothing
42
+
43
+ First establish whether the API waits for an effect. A returned client means sent/queued, not successful. `await` on a synchronous setter adds no confirmation.
44
+
45
+ | Action | Common cause |
46
+ |---------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
47
+ | Loader/pusher/hatch configuration throws an enum error | Numeric decoded/default enums are merged into builders requiring named enums. Supply all enum fields explicitly; see [machine limitations](MACHINES.md#loaders) |
48
+ | Launcher angle/power changes no intended target | Those commands use the active interaction. A launcher handle alone does not select it |
49
+ | Sign setter throws a display-mode error | Pass `SignDisplayMode.Always`, `WhenNear`, or `OnHover` explicitly; its numeric default is currently rejected by the named-mode builder |
50
+ | Sign/fabricator/comms operation affects the wrong interaction | Their messages also rely on active UI state; `open()` does not await panel readiness |
51
+ | Context-menu operation times out | Entity must exist in the current world, have a position, be in range, and expose the requested menu |
52
+ | Menu selection seems successful but effect is absent | `choose()` resolves when selection is sent, not after the action; use a specialized confirmation method where available |
53
+ | Inventory equip fails | The slot can be empty/non-equipment; infer from a known item or pass a valid explicit equipment slot |
54
+ | Model state ignores mutations to returned objects | Cached objects are not configuration commands; call the action methods |
55
+ | Combining separate movement/aim sends loses fields | Ordinary input fields default on each complete command; combine them in one call's command object |
56
+ | Rank/kick/ban fails despite local flags | Server captain hierarchy and permission checks are authoritative |
57
+
58
+ Range, permissions, inventory, Safe Zone status, cooldowns, and game rules are not bypassed by Dredless. Check [management completion conditions](MANAGEMENT.md#completion-and-shared-rules) and [machine targeting](MACHINES.md#targeting-and-completion).
59
+
60
+ ## Timeouts and retries
61
+
62
+ Management waits generally default to 10000 ms; tip operations default to 5000 ms per response. Only documented methods implement these waits. Raw requests and ordinary setters do not acquire a success promise by being awaited.
63
+
64
+ A timeout stops local waiting, not the server action. Check resulting state before repeating a save, account mutation, recovery, or other consequential request. Messages without request IDs can be mistaken for later retries' responses. Concurrent operations that share an active UI/menu can interfere even when the library does not reject them locally.
65
+
66
+ Watch `disconnect` separately from `error`; a socket error can precede its eventual close. Catch operation rejections and inspect `DisconnectError.disconnect` when available.
67
+
68
+ ## Browser and HTTP problems
69
+
70
+ Browser code needs access to the relevant origin/cookies. Loading a CDN script on another origin does not authorize access to Drednot. Workers, extension backgrounds, and popups can have different cookie access from a page/content script.
71
+
72
+ Use `createSession()` to inspect an existing ambient browser identity; `createAnonSession()` can change it. A blank JavaScript-readable cookie property does not prove the browser lacks an HttpOnly session.
73
+
74
+ For custom HTTP calls, `session.request()` JSON-stringifies ordinary objects. Send URL-encoded forms as strings with their form content type; native Fetch body types are not all supported by this wrapper. Check status and merge cookies yourself when using the raw helper.
75
+
76
+ Account methods scrape labeled HTML fields and forms. A login/challenge page, changed markup, missing clan form, or malformed perk list can reject the read. Perk removal cannot confirm success if the subsequent page omits a readable list. These methods have no built-in timeout/cancellation options. See [account limitations](ACCOUNT.md#failures-and-data-lifetime).
77
+
78
+ ## Useful diagnostics
79
+
80
+ **Fragment:** inspect bounded errors and normalized close details without dumping credential-bearing snapshots:
81
+
82
+ ```js
83
+ client.on("decode-error", (entry) => console.error(entry));
84
+ client.on("error", (error) => console.error(error.message));
85
+ client.on("disconnect", (info) => console.error(info));
86
+ console.log(client.debug.decodeErrors());
87
+ ```
88
+
89
+ For a bug report, include the package version, runtime/entrypoint, operation, relevant error/close code, and whether state was available before the action. A small sanitized capture can help protocol investigations. Session/connection snapshots and HARs can contain authentication cookies; avoid using an entire state dump as an ordinary diagnostic log.
90
+
91
+ [Protocol tools](PROTOCOL.md) describes frame decoding, world/model layers, and retained histories. The complete [declarations](../src/index.d.ts) are the field reference when inspecting a component not expanded in the prose guides.