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,285 @@
1
+ # Ship management
2
+
3
+ [Documentation index](README.md) · [Events](EVENTS.md#management-updates) · [Account perks](ACCOUNT.md#ships-with-patron-perks)
4
+
5
+ ## Contents
6
+
7
+ - [Completion and shared rules](#completion-and-shared-rules)
8
+ - [Read management state](#read-management-state)
9
+ - [Save a ship](#save-a-ship)
10
+ - [Rename a ship](#rename-a-ship)
11
+ - [Privacy and invite reset](#privacy-and-invite-reset)
12
+ - [Crew control](#crew-control)
13
+ - [Demote yourself](#demote-yourself)
14
+ - [Starter recovery](#starter-recovery)
15
+ - [Crew logs](#crew-logs)
16
+ - [Patron perks](#patron-perks)
17
+ - [Gravity](#gravity)
18
+
19
+ ## Completion and shared rules
20
+
21
+ Methods below live on `client.management`. Server permissions still apply; having a method does not grant captain/owner
22
+ access.
23
+
24
+ | Operation | Return | What has completed? |
25
+ |----------------------------------------|---------------------------|----------------------------------------------|
26
+ | `canSave()` | `boolean` | Local eligibility check only |
27
+ | `save(options?)` | `Promise<DisconnectInfo>` | Close with `Saved` code 4015 received |
28
+ | `rename(name, options?)` | `Promise<void>` | Permission allowed and commit sent |
29
+ | `resetInvite(options?)` | `Promise<ShipConfig>` | Changed nonempty invite key observed |
30
+ | `demoteSelf(options?)` | `Promise<void>` | Demotion received and caches cleared |
31
+ | `recoverStarterItem(itemId, options?)` | `Promise<void>` | Successful recovery response received |
32
+ | Other action methods | `DredlessClient` | Command sent/queued; no confirmation awaited |
33
+
34
+ The promise-based operations require a connected, ready, writable client. Options use `timeout` in milliseconds, default
35
+ **10000**, positive and at most **2147483647**. A second pending call of the same operation rejects; this is not a
36
+ general lock across all management actions.
37
+
38
+ Sending errors, disconnects, and timeouts reject. Disconnect failures use `DisconnectError` with `.disconnect`. Rename,
39
+ recovery, and invite-reset waits also reject when demotion interrupts them or their confirmation arrives for a changed
40
+ current world. Save relies on its close result; self-demotion waits for the demotion event.
41
+
42
+ Timeout does not undo a server action. Replies do not carry request identifiers, so a matching later event confirms
43
+ observed state, not necessarily a uniquely correlated request. Avoid competing raw requests and blind retries while
44
+ waiting.
45
+
46
+ Direct client aliases include `canSaveShip`, `saveShip`, `renameShip`, `resetInvite`, `demoteSelf`, and
47
+ `recoverStarterItem` with the same corresponding options/returns. Prefer the domain in application code.
48
+
49
+ ## Read management state
50
+
51
+ ```ts
52
+ client.management.config(): ShipConfig | null;
53
+ client.management.playerList(): ShipPlayerList | null;
54
+ client.management.crewLog(): CrewLog | null;
55
+ client.management.hasCheats(): boolean;
56
+ client.management.requestPlayerList(): DredlessClient;
57
+ ```
58
+
59
+ Config includes `privacy`, `privacyName`, `inviteKey`, `teamId`, and `patronPerks`. `hasCheats()` reads the cached
60
+ captain-subrank message's cheat flag; it is not a capability test for arbitrary commands.
61
+
62
+ Player-list messages update/remove individual entries and merge them into the cache. `ShipPlayerList` includes
63
+ `players`, latest nonremoved `changes`, `removedPlayers`, `ownerCaptainRank`, and derived `shipOwners`. Each player
64
+ entry has `refId`, identity/color, rank, captain rank, item/alias data, playtime, online count, and management flags.
65
+ Use `refId` for management commands, not an entity ID or `sid`.
66
+
67
+ `canBeManaged` and owner-related fields are derived hints; the server remains authoritative. The current normalization
68
+ marks players at the lowest positive captain rank as `shipOwners`; this can include tied ranks. It is not a guarantee
69
+ that the server recognizes a unique owner for an owner-only action.
70
+
71
+ Read cached data once and subscribe to `ship-config`, `captain-subrank`, `player-list`, and `crew-log`. They can contain
72
+ `null` after demotion. `requestPlayerList()` sends a refresh request; it does not return the response or wait for it.
73
+ Config/logs arrive through server session messages.
74
+
75
+ ## Save a ship
76
+
77
+ ```ts
78
+ client.management.canSave(): boolean;
79
+ client.management.save(options?: {timeout?: number}): Promise<DisconnectInfo>;
80
+ ```
81
+
82
+ `canSave()` requires a connected/ready client, a current ship with a known parent world, and parent `danger_level === 0`
83
+ (Safe Zone). Missing or unsafe metadata returns `false`. It does not check all server-side permission rules.
84
+
85
+ **Fragment:** this saves the ship and ejects its players:
86
+
87
+ ```js
88
+ if (!client.management.canSave()) throw new Error("Ship is not in a known Safe Zone");
89
+ const result = await client.management.save();
90
+ console.log(result.name); // Saved
91
+ ```
92
+
93
+ The promise resolves only when the socket closes with `DropReason.Saved` (4015). Any other close code rejects. If the
94
+ server keeps the connection open without confirming, the operation times out. After success the client is disconnected;
95
+ join/start a new client to continue using the ship.
96
+
97
+ ## Rename a ship
98
+
99
+ ```ts
100
+ client.management.rename(name: string, options?: {color?: string; timeout?: number}): Promise<void>;
101
+ ```
102
+
103
+ Name must be a string of at most **32 UTF-16 code units**, matching the official input limit. It is not automatically
104
+ trimmed; the client permits an empty string, with final validation left to the server. Optional color is a six-digit
105
+ `#RRGGBB` string; omit it to preserve the color from the permission response.
106
+
107
+ **Fragment:**
108
+
109
+ ```js
110
+ await client.management.rename("New Name", {color: "#3a8bce"});
111
+ ```
112
+
113
+ The helper requests permission, waits for `rename_response`, then sends the commit. A denial rejects with the server's
114
+ message. There is no dedicated commit-success acknowledgment handled by the official client; this promise resolves when
115
+ the commit is sent. Observe ship metadata for the eventual name/color.
116
+
117
+ ## Privacy and invite reset
118
+
119
+ ```ts
120
+ client.management.setPrivacy(privacy: ShipPrivacy): DredlessClient;
121
+ client.management.resetInvite(options?: {timeout?: number}): Promise<ShipConfig>;
122
+
123
+ type ShipPrivacy = 0 | 1 | boolean | "public" | "private";
124
+ ```
125
+
126
+ `0`, `false`, and `"public"` select public; `1`, `true`, and `"private"` select private. The privacy setter does not
127
+ await confirmation; read `config()` or `ship-config`.
128
+
129
+ **Fragment:** resetting the invite invalidates its previous link:
130
+
131
+ ```js
132
+ const config = await client.management.resetInvite();
133
+ console.log(config.inviteKey);
134
+ ```
135
+
136
+ Reset waits for a config whose nonempty invite key differs from the key cached when the request began. Unchanged-key
137
+ config updates are ignored. If no previous key was known, the first subsequent nonempty key satisfies the wait. It
138
+ returns the updated config; it does not prove that a particular request caused that update.
139
+
140
+ ## Crew control
141
+
142
+ ```ts
143
+ client.management.setPlayerRank(refId: number, rank: ShipPlayerRank): DredlessClient;
144
+ client.management.kickPlayer(refId: number): DredlessClient;
145
+ client.management.banPlayer(refId: number): DredlessClient;
146
+ client.management.unbanPlayer(refId: number): DredlessClient;
147
+
148
+ type ShipPlayerRank = 0 | 1 | 3 | "guest" | "crew" | "captain";
149
+ ```
150
+
151
+ Rank values are guest 0, crew 1, captain 3. IDs must be nonnegative integers. These calls send immediately and do not
152
+ await a player-list change. Observe the merged list and server messages. A locally plausible `canBeManaged` value does
153
+ not override server restrictions.
154
+
155
+ **Fragment:** `selectedRefId` must come from a player-list entry selected by your application:
156
+
157
+ ```js
158
+ const player = client.management.playerList()?.players.find((entry) => entry.refId === selectedRefId);
159
+ if (!player || player.refId === null) throw new Error("No matching management entry");
160
+ client.management.unbanPlayer(player.refId);
161
+ ```
162
+
163
+ Player-rank changes are different from [door/spawn access restrictions](MACHINES.md#context-menus-and-access).
164
+
165
+ ## Demote yourself
166
+
167
+ ```ts
168
+ client.management.demoteSelf(options?: {timeout?: number}): Promise<void>;
169
+ ```
170
+
171
+ This requests demotion to Guest. It can remove the account's ability to load/manage the ship and can revoke its patron
172
+ contribution. The server's demotion response is the confirmation, not merely sending the command.
173
+
174
+ Before emitting `demote`, Dredless clears ship config, captain subrank/cheat flags, player list, and crew log. It then
175
+ emits their cache events with `null`. Pending rename/recovery/invite-reset waits reject. Player entity rank is still
176
+ driven by subsequent model updates; cache clearing does not fabricate a model update.
177
+
178
+ If the server does not send a demotion response, the promise times out. The helper does not manufacture a success result
179
+ from an unrelated captain-subrank value.
180
+
181
+ ## Starter recovery
182
+
183
+ ```ts
184
+ client.management.recoverStarterItem(itemId: number,
185
+ options?: {timeout?: number}): Promise<void>;
186
+ ```
187
+
188
+ Use an actual starter-recovery item ID supported by the server. IDs must be nonnegative integers. Recovery is an
189
+ owner-controlled server action: it deletes existing instances of the relevant items from the ship and returns them to
190
+ the caller's inventory. The official UI describes a once-per-minute limit; the server enforces eligibility and cooldown.
191
+
192
+ The promise waits for `starter_recovery_response`. Missing/null `fail_reason` resolves; otherwise it rejects with the
193
+ server's failure message. Inventory/model effects arrive through their normal updates. Every response also emits
194
+ `starter-recovery` with `{success, failReason}` and the original session packet, including responses to lower-level
195
+ requests.
196
+
197
+ ## Crew logs
198
+
199
+ ```ts
200
+ client.management.crewLog(): CrewLog | null;
201
+
202
+ interface CrewLogEntry {
203
+ icon: string | null;
204
+ text: string;
205
+ rawText: unknown;
206
+ count: number;
207
+ refId: number | null;
208
+ secondaryRefId: number | null;
209
+ }
210
+ interface CrewLog {
211
+ entries: CrewLogEntry[];
212
+ changes: CrewLogEntry[];
213
+ reset: boolean;
214
+ amend: boolean;
215
+ }
216
+ ```
217
+
218
+ Logs are pushed as captain/session `log` messages. There is no separate log-fetch request in the official client. Before
219
+ the first message, the cache is `null`; a received empty log is a non-null object with `entries: []`.
220
+
221
+ Apply each message in order: clear for `reset`, remove the last remaining entry for `amend`, append the new entries,
222
+ then retain the newest **500**. `entries` is the resulting log, oldest first; `changes` is just the entries supplied by
223
+ the latest message. An empty reset/amend can still change state. Counts aggregate repeated actions and do not expand the
224
+ array.
225
+
226
+ `text` flattens supported rich-text content. Styled content, date/image/badge tokens remain available in `rawText` for
227
+ custom rendering. `icon` remains an asset path. `refId`/`secondaryRefId` preserve server player references used to
228
+ filter the official player list.
229
+
230
+ **Fragment:**
231
+
232
+ ```js
233
+ const report = (log) => console.log(log?.entries ?? []);
234
+ client.on("crew-log", report);
235
+ report(client.management.crewLog());
236
+ ```
237
+
238
+ The event's second argument is the original session packet. The cache is also available at `client.crewLog` and
239
+ `client.state().crewLog`. Demotion emits `crew-log` with `null`; disconnect clears the cache before the disconnect
240
+ event.
241
+
242
+ ## Patron perks
243
+
244
+ ```ts
245
+ client.management.setPatronPerks(enabled: boolean): DredlessClient;
246
+ client.management.enablePatronPerks(): DredlessClient;
247
+ client.management.disablePatronPerks(): DredlessClient;
248
+ client.management.patronPerks(): ShipPatronPerk[] | null;
249
+ client.management.hasPatronPerks(name?: string): boolean;
250
+ ```
251
+
252
+ Setters apply/remove the caller's contribution on the current ship and return immediately. The direct client alias is `setShipPatronPerks(enabled)`. `enabled` must be a boolean;
253
+ the aliases pass true/false. The server checks account eligibility, permissions, and available perk allocations.
254
+ Disabling does not remove other patrons' contributions. Patron items can despawn after perks are removed.
255
+
256
+ `patronPerks()` reads config's `{name, color, tier}[]`: `null` if config is unavailable, `[]` if known empty. Color is
257
+ packed RGB; known tiers are `bronze`, `silver`, `gold`, `plat`, and `flux`. `hasPatronPerks()` checks any contributor; a
258
+ supplied string checks an exact, case-sensitive name. Unknown config returns `false`.
259
+
260
+ `patron-perks-response` emits `{failure}` plus the packet for every `perk_response`. A true flag reports failure; a
261
+ false flag alone is not a current applied-patrons list. Observe `ship-config` for that state. Setters do not
262
+ optimistically change the list.
263
+
264
+ To remove your perks from a listed ship without joining it, use the
265
+ separate [account method](ACCOUNT.md#ships-with-patron-perks).
266
+
267
+ ## Gravity
268
+
269
+ ```ts
270
+ client.management.setGravity(direction: ShipGravityDirection): DredlessClient;
271
+ type ShipGravityDirection = "down" | "up" | "left" | "right" | 0 | 1 | 2 | 3;
272
+ ```
273
+
274
+ This sends the official `coolsnake303` gravity control immediately, without awaiting confirmation. The search string
275
+ reveals controls in the official UI; it is not an unlock request required by the protocol.
276
+
277
+ | Constant | String | Wire value |
278
+ |--------------------------|-----------|------------|
279
+ | `GravityDirection.Down` | `"down"` | 0 |
280
+ | `GravityDirection.Up` | `"up"` | 1 |
281
+ | `GravityDirection.Left` | `"left"` | 2 |
282
+ | `GravityDirection.Right` | `"right"` | 3 |
283
+
284
+ Values must be exactly one of these lowercase names or numeric values. This mapping differs from `FixedAngleDirection`
285
+ used by machine configuration. Direct client alias: `setShipGravity(direction)`.