dredless 0.7.0 → 1.0.0
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.
- package/README.md +22 -57
- package/dist/browser/dredless.global.js +794 -98
- package/dist/browser/dredless.global.js.map +4 -4
- package/docs/ACCOUNT.md +146 -0
- package/docs/API_REFERENCE.md +63 -2563
- package/docs/BROWSER_API.md +151 -70
- package/docs/CLIENT.md +183 -0
- package/docs/EVENTS.md +136 -0
- package/docs/GETTING_STARTED.md +135 -0
- package/docs/MACHINES.md +351 -0
- package/docs/MANAGEMENT.md +285 -0
- package/docs/NODE_API.md +120 -318
- package/docs/PLAYER_INVENTORY.md +235 -0
- package/docs/PROTOCOL.md +227 -0
- package/docs/README.md +44 -0
- package/docs/TROUBLESHOOTING.md +91 -0
- package/docs/WORLD_STATE.md +183 -0
- package/package.json +1 -2
- package/src/browser.d.ts +31 -1
- package/src/browser.js +10 -0
- package/src/client/core.js +302 -15
- package/src/client/domains/index.js +57 -7
- package/src/client/index.d.ts +1 -1
- package/src/index.d.ts +299 -13
- package/src/index.js +10 -0
- package/src/network/account.js +182 -0
- package/src/network/disconnect.d.ts +2 -0
- package/src/network/disconnect.js +53 -0
- package/src/network/index.d.ts +1 -0
- package/src/network/index.js +1 -0
- package/src/network/invites.d.ts +0 -3
- package/src/network/invites.js +3 -40
- package/src/network/session.d.ts +2 -0
- package/src/network/session.js +21 -0
- package/src/protocol/outbound/ship-management.d.ts +66 -1
- package/src/protocol/outbound/ship-management.js +83 -0
- package/src/state/model/decoder.js +70 -10
- package/src/state/model/index.d.ts +4 -0
- package/src/state/model/state.js +22 -7
- package/src/state/world/index.js +27 -1
package/docs/BROWSER_API.md
CHANGED
|
@@ -1,100 +1,181 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Browser setup
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[Documentation index](README.md) · [Getting started](GETTING_STARTED.md) · [API reference](API_REFERENCE.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Execution context](#execution-context)
|
|
8
|
+
- [Bundled imports](#bundled-imports)
|
|
9
|
+
- [Global script](#global-script)
|
|
10
|
+
- [Session methods](#session-methods)
|
|
11
|
+
- [Connection methods](#connection-methods)
|
|
12
|
+
- [Attach to an existing socket](#attach-to-an-existing-socket)
|
|
13
|
+
|
|
14
|
+
## Execution context
|
|
15
|
+
|
|
16
|
+
Browser sessions use the browser's ambient cookie jar with `credentials: "include"`. They do not isolate identities like
|
|
17
|
+
Node sessions. Changing an anonymous key affects the ambient session.
|
|
18
|
+
|
|
19
|
+
Use a Drednot page integration or a content-script context with access to the site's cookies and origin. Background
|
|
20
|
+
pages, popups, workers, and unrelated origins may lack that access. Importing the package does not bypass browser
|
|
21
|
+
cookie, origin, or CORS restrictions.
|
|
22
|
+
|
|
23
|
+
Browsers manage `Cookie` and `Set-Cookie` themselves. JavaScript cannot install a separate cookie jar or custom
|
|
24
|
+
authentication headers on a browser WebSocket.
|
|
25
|
+
|
|
26
|
+
## Bundled imports
|
|
6
27
|
|
|
7
28
|
```js
|
|
8
|
-
import Dredless, {
|
|
29
|
+
import Dredless, {BrowserSession, DredlessClient} from "dredless/browser";
|
|
9
30
|
```
|
|
10
31
|
|
|
11
|
-
|
|
32
|
+
Prefer this explicit entrypoint for browser types. Shared machine constants and protocol tools are named exports; the
|
|
33
|
+
default export is the runtime namespace, not the entire module.
|
|
34
|
+
|
|
35
|
+
**Fragment:** use the site's existing login without creating a new anonymous identity:
|
|
12
36
|
|
|
13
37
|
```js
|
|
14
|
-
const session = await Dredless.
|
|
38
|
+
const session = await Dredless.createSession();
|
|
15
39
|
const servers = await Dredless.fetchServers();
|
|
16
|
-
const
|
|
17
|
-
|
|
40
|
+
const server = servers[0];
|
|
41
|
+
if (!server) throw new Error("No servers found");
|
|
42
|
+
console.log(await session.fetchShips(server));
|
|
18
43
|
```
|
|
19
44
|
|
|
20
|
-
|
|
45
|
+
## Global script
|
|
21
46
|
|
|
22
|
-
|
|
47
|
+
Run this only in an appropriate browser context. Use a bundle version that matches the API you intend to call; an
|
|
48
|
+
unversioned CDN URL follows published releases rather than this repository's current checkout.
|
|
23
49
|
|
|
24
|
-
|
|
50
|
+
```html
|
|
25
51
|
|
|
26
|
-
|
|
52
|
+
<script src="https://cdn.jsdelivr.net/npm/dredless/dist/browser/dredless.global.js"></script>
|
|
53
|
+
<script>
|
|
54
|
+
(async () => {
|
|
55
|
+
const session = await Dredless.createSession();
|
|
56
|
+
console.log((await session.fetchAccountStatus()).account);
|
|
57
|
+
})().catch(console.error);
|
|
58
|
+
</script>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The global is `Dredless`, containing the browser runtime namespace. It does not expose every named ESM export. Use
|
|
62
|
+
bundled imports when you need additional named helpers.
|
|
63
|
+
|
|
64
|
+
## Session methods
|
|
27
65
|
|
|
28
66
|
```ts
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
new BrowserSession(baseUrl ? : string)
|
|
68
|
+
:
|
|
69
|
+
BrowserSession;
|
|
70
|
+
Dredless.browserSession(baseUrl ? : string)
|
|
71
|
+
:
|
|
72
|
+
BrowserSession;
|
|
73
|
+
Dredless.createBrowserSession(baseUrl ? : string)
|
|
74
|
+
:
|
|
75
|
+
BrowserSession;
|
|
76
|
+
Dredless.setBrowserSession(session ? : BrowserSession | null)
|
|
77
|
+
:
|
|
78
|
+
BrowserSession | null;
|
|
79
|
+
Dredless.createSession(noticeVersion ? : number | null, baseUrl ? : string)
|
|
80
|
+
:
|
|
81
|
+
Promise<BrowserSession>;
|
|
82
|
+
Dredless.createAnonSession(anonKey ? : string | null, noticeVersion ? : number | null,
|
|
83
|
+
baseUrl ? : string)
|
|
84
|
+
:
|
|
85
|
+
Promise<BrowserSession>;
|
|
86
|
+
Dredless.fetchAnonKey(noticeVersion ? : number | null, baseUrl ? : string)
|
|
87
|
+
:
|
|
88
|
+
Promise<string>;
|
|
89
|
+
Dredless.createAnonToken(noticeVersion ? : number | null, baseUrl ? : string)
|
|
90
|
+
:
|
|
91
|
+
Promise<string>;
|
|
92
|
+
|
|
93
|
+
session.fetchAccountStatus()
|
|
94
|
+
:
|
|
95
|
+
Promise<unknown>;
|
|
96
|
+
session.readAnonKey()
|
|
97
|
+
:
|
|
98
|
+
Promise<string>;
|
|
99
|
+
session.fetchAnonKey(noticeVersion ? : number | null)
|
|
100
|
+
:
|
|
101
|
+
Promise<string>;
|
|
53
102
|
```
|
|
54
103
|
|
|
55
|
-
`createSession(
|
|
104
|
+
`createSession()` refreshes status on the ambient session. `createAnonSession()` applies a supplied key or posts an
|
|
105
|
+
anonymous login, then refreshes status. `fetchAnonKey()`/`createAnonToken()` request a new key. Key retrieval uses
|
|
106
|
+
`cookieStore` or `document.cookie`; it rejects when the key is not readable even if the login request succeeded.
|
|
56
107
|
|
|
57
|
-
`
|
|
108
|
+
`BrowserSession` inherits [account methods](ACCOUNT.md), HTTP `request()`, ship reads, and client factories from the
|
|
109
|
+
session API. It uses browser-owned cookies and does not expose a registered-account login flow. Its JavaScript-readable
|
|
110
|
+
`gameSession`/`gameToken` properties may be empty even when browser authentication works.
|
|
58
111
|
|
|
59
|
-
|
|
112
|
+
## Connection methods
|
|
60
113
|
|
|
61
|
-
|
|
114
|
+
Session methods have the same arguments as [Node session methods](NODE_API.md#join-start-and-invites). Top-level browser
|
|
115
|
+
helpers take the ambient session as an optional final argument:
|
|
62
116
|
|
|
63
|
-
|
|
117
|
+
```ts
|
|
118
|
+
Dredless.fetchShips(server
|
|
119
|
+
:
|
|
120
|
+
ServerRef, session ? : BrowserSession | null
|
|
121
|
+
):
|
|
122
|
+
Promise<Ship[]>;
|
|
123
|
+
Dredless.fetchShipList(server
|
|
124
|
+
:
|
|
125
|
+
ServerRef, session ? : BrowserSession | null
|
|
126
|
+
):
|
|
127
|
+
Promise<ShipList>;
|
|
128
|
+
Dredless.fetchAccountStatus(session ? : BrowserSession | null)
|
|
129
|
+
:
|
|
130
|
+
Promise<unknown>;
|
|
131
|
+
Dredless.joinShip(server
|
|
132
|
+
:
|
|
133
|
+
ServerRef, ship ? : ShipRef, session ? : BrowserSession | null
|
|
134
|
+
):
|
|
135
|
+
Promise<DredlessClient>;
|
|
136
|
+
Dredless.startShip(server
|
|
137
|
+
:
|
|
138
|
+
ServerRef, ship ? : ShipRef, session ? : BrowserSession | null
|
|
139
|
+
):
|
|
140
|
+
Promise<DredlessClient>;
|
|
141
|
+
Dredless.startNewShip(server
|
|
142
|
+
:
|
|
143
|
+
ServerRef, name ? : string, color ? : string,
|
|
144
|
+
session ? : BrowserSession | null
|
|
145
|
+
):
|
|
146
|
+
Promise<DredlessClient>;
|
|
147
|
+
Dredless.joinInvite(server
|
|
148
|
+
:
|
|
149
|
+
ServerRef, code
|
|
150
|
+
:
|
|
151
|
+
string, session ? : BrowserSession | null
|
|
152
|
+
):
|
|
153
|
+
Promise<DredlessClient>;
|
|
154
|
+
```
|
|
64
155
|
|
|
65
|
-
|
|
156
|
+
The HTTP join/start distinctions and readiness conditions are the same as Node. `fetchServers`, `fetchServerStatus`,
|
|
157
|
+
`fetchServerStatuses`, `fetchGameVersion`, and `fetchNoticeVersion` are also on the browser namespace. The browser
|
|
158
|
+
namespace does not contain the Node `Session`/`AnonSession` constructors.
|
|
66
159
|
|
|
67
|
-
##
|
|
160
|
+
## Attach to an existing socket
|
|
68
161
|
|
|
69
|
-
`
|
|
162
|
+
**Fragment:** `socket` is a WebSocket you already obtained from your integration. Attach early: Dredless cannot
|
|
163
|
+
reconstruct frames the socket received before attachment.
|
|
70
164
|
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
baseUrl: string;
|
|
75
|
-
ambientAuth: true;
|
|
76
|
-
|
|
77
|
-
readAnonKey(): Promise<string>;
|
|
78
|
-
fetchAnonKey(noticeVersion?: number | null): Promise<string>;
|
|
79
|
-
fetchAccountStatus(): Promise<unknown>;
|
|
80
|
-
fetchShips(server: ServerRef): Promise<Ship[]>;
|
|
81
|
-
fetchShipList(server: ServerRef): Promise<ShipList>;
|
|
82
|
-
|
|
83
|
-
joinShipConnection(server: ServerRef, ship?: ShipRef): Promise<Connection>;
|
|
84
|
-
startShipConnection(server: ServerRef, ship?: ShipRef): Promise<Connection>;
|
|
85
|
-
startNewShipConnection(server: ServerRef, name?: string, color?: string): Promise<Connection>;
|
|
86
|
-
joinInviteConnection(server: ServerRef, code: string): Promise<Connection>;
|
|
87
|
-
|
|
88
|
-
joinShip(server: ServerRef, ship?: ShipRef): Promise<DredlessClient>;
|
|
89
|
-
startShip(server: ServerRef, ship?: ShipRef): Promise<DredlessClient>;
|
|
90
|
-
startNewShip(server: ServerRef, name?: string, color?: string): Promise<DredlessClient>;
|
|
91
|
-
joinInvite(server: ServerRef, code: string): Promise<DredlessClient>;
|
|
92
|
-
}
|
|
165
|
+
```js
|
|
166
|
+
const client = DredlessClient.attachWebSocket(socket, {mode: "readonly"});
|
|
167
|
+
client.on("model", ({world}) => console.log(world.id, world.model.players()));
|
|
93
168
|
```
|
|
94
169
|
|
|
170
|
+
| Mode | Writes | Automatically sends hello/bootstrap/keepalive | `close()` |
|
|
171
|
+
|-----------------------|----------|-----------------------------------------------|-------------------------------|
|
|
172
|
+
| `"observe"` (default) | Allowed | No | Can close the attached socket |
|
|
173
|
+
| `"bootstrap"` | Allowed | Yes | Can close the attached socket |
|
|
174
|
+
| `"readonly"` | Rejected | No | No-op |
|
|
95
175
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
Browser start/join helpers still produce `Connection` objects internally, but a browser `Connection` may have an empty `gameToken` when the token is only available through browser-managed cookies. `DredlessClient` uses the browser `WebSocket` implementation and relies on browser-managed cookies and origin headers.
|
|
176
|
+
Use `readonly` for observation. `observe` permits explicit writes despite its name. Avoid two clients independently
|
|
177
|
+
sending initialization or input to one socket.
|
|
99
178
|
|
|
100
|
-
|
|
179
|
+
For an already-initialized socket, `ready: true` and `sid` can supply known protocol readiness metadata. They do not
|
|
180
|
+
recover missing world state. See [attachment options](CLIENT.md#attach-an-existing-websocket)
|
|
181
|
+
and [event timing](EVENTS.md#ordering-and-listeners).
|
package/docs/CLIENT.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Client lifecycle and networking
|
|
2
|
+
|
|
3
|
+
[Documentation index](README.md) · [Events](EVENTS.md) · [API reference](API_REFERENCE.md)
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Construction and retention](#construction-and-retention)
|
|
8
|
+
- [Completion and readiness](#completion-and-readiness)
|
|
9
|
+
- [Attach an existing WebSocket](#attach-an-existing-websocket)
|
|
10
|
+
- [Disconnects](#disconnects)
|
|
11
|
+
- [Send commands and messages](#send-commands-and-messages)
|
|
12
|
+
- [Inspect client state](#inspect-client-state)
|
|
13
|
+
|
|
14
|
+
## Construction and retention
|
|
15
|
+
|
|
16
|
+
Normally obtain a client from a [session factory](NODE_API.md#join-start-and-invites). Construct one yourself when you
|
|
17
|
+
need listeners installed before the connection becomes ready:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
new DredlessClient(connection: Connection, options?: DredlessClientOptions): DredlessClient;
|
|
21
|
+
|
|
22
|
+
interface DredlessClientOptions {
|
|
23
|
+
connect?: boolean; // true
|
|
24
|
+
packetHistory?: number; // 200 packets
|
|
25
|
+
eventHistory?: number; // 200 events per WorldState
|
|
26
|
+
modelPacketHistory?: number; // 100 packets per WorldState
|
|
27
|
+
chunkHistory?: number; // 64 chunks per WorldState
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
client.whenReady(): Promise<DredlessClient>;
|
|
31
|
+
client.close(code?: number, reason?: string): DredlessClient;
|
|
32
|
+
client.disconnect(code?: number, reason?: string): DredlessClient;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The constructor connects immediately unless `connect: false`. That option supports offline inspection; it does not
|
|
36
|
+
provide a later public `connect()` method. History limits bound retained debugging buffers, not the live decoded world.
|
|
37
|
+
`0` disables a buffer; `Infinity` makes it unbounded. Negative/non-finite values fall back to defaults, except the
|
|
38
|
+
explicit `Infinity` case.
|
|
39
|
+
|
|
40
|
+
`close()` defaults to code `1000` and reason `"client"`; `disconnect()` is an alias. Readonly attachments make closing a
|
|
41
|
+
no-op. For writable clients, closure state changes when the socket close handler runs.
|
|
42
|
+
|
|
43
|
+
## Completion and readiness
|
|
44
|
+
|
|
45
|
+
`whenReady()` resolves when the server's ready packet arrives. Session client factories await it before returning. A
|
|
46
|
+
close before readiness rejects with `DisconnectError`; connection/decoding setup failures can reject with other errors.
|
|
47
|
+
|
|
48
|
+
Readiness is not a guarantee that all entities, model components, inventory, or interaction panels are populated.
|
|
49
|
+
`currentShip()` and individual fields can be missing. Read existing caches and subscribe to
|
|
50
|
+
relevant [events](EVENTS.md). Install listeners using the connection-descriptor pattern if you need the earliest
|
|
51
|
+
packets.
|
|
52
|
+
|
|
53
|
+
Most synchronous actions return the client and send or queue a command. They do not verify its effect. Commands
|
|
54
|
+
generally queue until connected, ready, and carrying a session ID; decoded messages queue until connected and, by
|
|
55
|
+
default, ready. Writes after a disconnect reject rather than being queued for reconnection. Promise-based
|
|
56
|
+
management/menu methods have their own readiness checks, timeout, and overlap rules.
|
|
57
|
+
|
|
58
|
+
| Example | Completion meaning |
|
|
59
|
+
|-----------------------------|----------------------------------------|
|
|
60
|
+
| `loader.configure(config)` | Configuration sent/queued |
|
|
61
|
+
| `tips.choose(entity, text)` | Menu received and selection sent |
|
|
62
|
+
| `door.setRank("crew")` | Requested rank observed in model state |
|
|
63
|
+
| `management.rename(name)` | Rename allowed and commit sent |
|
|
64
|
+
| `management.resetInvite()` | A changed nonempty invite key observed |
|
|
65
|
+
| `management.save()` | Server closed with `DropReason.Saved` |
|
|
66
|
+
|
|
67
|
+
A timeout ends local waiting; it does not cancel an action already processed by the server. Do not automatically repeat
|
|
68
|
+
a mutation solely because its confirmation timed out.
|
|
69
|
+
|
|
70
|
+
## Attach an existing WebSocket
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
DredlessClient.attachWebSocket(socket: unknown, options?: AttachWebSocketOptions): DredlessClient;
|
|
74
|
+
|
|
75
|
+
interface AttachWebSocketOptions {
|
|
76
|
+
mode?: "observe" | "bootstrap" | "readonly";
|
|
77
|
+
connected?: boolean | null;
|
|
78
|
+
ready?: boolean;
|
|
79
|
+
sid?: number | null;
|
|
80
|
+
baseUrl?: string | null;
|
|
81
|
+
session?: Session | null;
|
|
82
|
+
serverId?: number | null;
|
|
83
|
+
server?: Server | null;
|
|
84
|
+
netPort?: number | null;
|
|
85
|
+
gameToken?: string;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Modes and browser ownership concerns are covered in [browser attachment](BROWSER_API.md#attach-to-an-existing-socket).
|
|
90
|
+
Metadata does not reconstruct frames received before attachment. `ready: true`/`sid` should describe an
|
|
91
|
+
already-initialized protocol session, not invent a new one.
|
|
92
|
+
|
|
93
|
+
## Disconnects
|
|
94
|
+
|
|
95
|
+
Listen to `"disconnect"` for normalized information:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
interface DisconnectInfo {
|
|
99
|
+
readonly code: number;
|
|
100
|
+
readonly name: keyof typeof DropReason | null;
|
|
101
|
+
readonly reason: string;
|
|
102
|
+
readonly wasClean: boolean | null;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Fragment:**
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
client.on("disconnect", (info) => {
|
|
110
|
+
console.log(info.name, info.code, info.reason);
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The close handler first marks the client disconnected/not ready, clears `sid`, pending writes, keepalive,
|
|
115
|
+
management/panel state, and command acknowledgments. It sets `client.lastDisconnect` and `client.net.lastDisconnect`,
|
|
116
|
+
then emits `"disconnect"` and the original `"close"` arguments. World data and historical buffers remain available as
|
|
117
|
+
last-known state, not evidence of a live connection.
|
|
118
|
+
|
|
119
|
+
There is no automatic reconnect. Create/join a new client and reinstall subscriptions when your application decides to
|
|
120
|
+
reconnect.
|
|
121
|
+
|
|
122
|
+
`DisconnectError` carries the normalized record in `.disconnect`. `normalizeDisconnect()` accepts a browser close event
|
|
123
|
+
or Node-style code/reason. Missing codes become `1006`; unknown/standard codes have `name: null`. `wasClean` is `null`
|
|
124
|
+
when the socket API does not supply it.
|
|
125
|
+
|
|
126
|
+
| Code | `DropReason` name | Code | `DropReason` name |
|
|
127
|
+
|------|-------------------|------|-------------------|
|
|
128
|
+
| 4000 | ProtocolError | 4010 | Idle |
|
|
129
|
+
| 4001 | BadVersion | 4011 | ShipDestruct |
|
|
130
|
+
| 4002 | ConnectLimit | 4012 | JoinBlocked |
|
|
131
|
+
| 4003 | ConnectRate | 4013 | HardBanned |
|
|
132
|
+
| 4004 | TeamLimit | 4014 | NoLoad |
|
|
133
|
+
| 4005 | FullPlayers | 4015 | Saved |
|
|
134
|
+
| 4006 | FullTeams | 4016 | InvalidInvite |
|
|
135
|
+
| 4007 | TeamNotAvailable | 4017 | Shutdown |
|
|
136
|
+
| 4008 | Kicked | 4018 | ServerError |
|
|
137
|
+
| 4009 | Banned | 4019 | GTFO |
|
|
138
|
+
|
|
139
|
+
These exports are available from Node, browser, and `dredless/network`.
|
|
140
|
+
|
|
141
|
+
## Send commands and messages
|
|
142
|
+
|
|
143
|
+
Prefer player, machine, and management methods for normal actions. `client.net` is the lower-level transport domain:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
client.net.send(command?: Command): DredlessClient;
|
|
147
|
+
client.net.sendMessage(message: unknown, options?: {afterReady?: boolean}): DredlessClient;
|
|
148
|
+
client.net.sendRaw(message: unknown, options?: {afterReady?: boolean}): DredlessClient;
|
|
149
|
+
client.net.sendEntityCommand(cmd: string, args?: unknown[]): DredlessClient;
|
|
150
|
+
client.net.sendUiConfig(data: unknown): DredlessClient;
|
|
151
|
+
client.net.sendBlueprintPlacement(placement: BlueprintPlacement): DredlessClient;
|
|
152
|
+
client.net.setOutfit(outfit: unknown): DredlessClient;
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`send()` applies input settings, fills command defaults, and signs the command. It is for player-input packets.
|
|
156
|
+
`sendMessage()` MessagePack-encodes a decoded protocol object. `afterReady` defaults to `true`.
|
|
157
|
+
|
|
158
|
+
Despite its name, `sendRaw()` is an alias of `sendMessage()`: it does not transmit pre-encoded bytes unchanged. Do not
|
|
159
|
+
pass the output of `encodeMsgpack()` to it expecting raw transport. UI config byte arrays are payloads passed to
|
|
160
|
+
`sendUiConfig()`, not whole WebSocket frames.
|
|
161
|
+
|
|
162
|
+
`net` also exposes `connected`, `ready`, `sid`, `packetCount`, `lastPacket`, and `lastDisconnect`. Direct client
|
|
163
|
+
transport methods exist too; using the domain makes intent clearer.
|
|
164
|
+
|
|
165
|
+
## Inspect client state
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
client.state(options?: {includeTiles?: boolean; includeModel?: boolean}): ClientSnapshot;
|
|
169
|
+
client.snapshot(options?: {includeTiles?: boolean; includeModel?: boolean}): ClientSnapshot;
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The implementation exposes `snapshot()` as well as `state()`. The result groups connection metadata, worlds, inventory,
|
|
173
|
+
panels, management caches (including `crewLog`), recent packet/event buffers, acknowledgments, and decode errors. Full
|
|
174
|
+
fields live in [ClientSnapshot](../src/index.d.ts).
|
|
175
|
+
|
|
176
|
+
Client/world snapshots are not uniformly cloned or frozen. For example, `client.state().inventory` and management
|
|
177
|
+
objects are shared references; don't mutate them. [World state](WORLD_STATE.md#live-reads-and-snapshots) distinguishes
|
|
178
|
+
frozen entity snapshots from aggregate state. Snapshots may contain session credentials through `session`/`connection`;
|
|
179
|
+
select fields before logging.
|
|
180
|
+
|
|
181
|
+
`includeModel` includes raw model tables, and `includeTiles` includes tile values. For blocks, use a world snapshot with
|
|
182
|
+
`includeBlocks: true`; the client snapshot implementation currently does not forward `includeBlocks`, even though its
|
|
183
|
+
declaration lists the option.
|
package/docs/EVENTS.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Event catalog
|
|
2
|
+
|
|
3
|
+
[Documentation index](README.md) · [Client lifecycle](CLIENT.md) · [API reference](API_REFERENCE.md)
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Ordering and listeners](#ordering-and-listeners)
|
|
8
|
+
- [Lifecycle and transport](#lifecycle-and-transport)
|
|
9
|
+
- [World updates](#world-updates)
|
|
10
|
+
- [Player and interaction updates](#player-and-interaction-updates)
|
|
11
|
+
- [Management updates](#management-updates)
|
|
12
|
+
- [Errors](#errors)
|
|
13
|
+
|
|
14
|
+
## Ordering and listeners
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
client.on(type: string, callback: (...args: any[]) => void): DredlessClient;
|
|
18
|
+
client.off(type: string, callback: (...args: any[]) => void): DredlessClient;
|
|
19
|
+
client.once(type: string, callback: (...args: any[]) => void): DredlessClient;
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Callbacks run synchronously. Returned promises are not awaited, so handle errors inside async listeners. Keep frequent
|
|
23
|
+
model/packet listeners short. Ordinary listener exceptions are forwarded to an `"error"` listener when one exists;
|
|
24
|
+
otherwise they can interrupt dispatch. Errors encountered during packet processing can be recorded as decode errors.
|
|
25
|
+
|
|
26
|
+
Events are not replayed when you subscribe. Factory methods wait for readiness, so initial world, inventory, or
|
|
27
|
+
management events may have happened before you receive the client. Subscribe and also read the corresponding cache. For
|
|
28
|
+
earliest-event capture, construct a client from a [Connection descriptor](NODE_API.md#construct-a-client-yourself).
|
|
29
|
+
|
|
30
|
+
**Fragment:** keep a callback reference to remove an `on()` listener:
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
const report = () => console.log(client.management.crewLog()?.entries.length ?? 0);
|
|
34
|
+
client.on("crew-log", report);
|
|
35
|
+
report();
|
|
36
|
+
// Later:
|
|
37
|
+
client.off("crew-log", report);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`once()` removes its wrapper before invoking it. `off()` expects the registered function identity; it does not cancel a
|
|
41
|
+
`once()` wrapper using the original callback. A manually emitted event does not update state or simulate an authentic
|
|
42
|
+
server response.
|
|
43
|
+
|
|
44
|
+
## Lifecycle and transport
|
|
45
|
+
|
|
46
|
+
| Event | Callback arguments | Timing/meaning |
|
|
47
|
+
|--------------|----------------------------------------------------|---------------------------------------------------------------------------------|
|
|
48
|
+
| `attach` | `(client)` | Attached socket initialized; can fire during `attachWebSocket()` |
|
|
49
|
+
| `open` | `(client)` | Socket marked connected; can fire during attachment |
|
|
50
|
+
| `ready` | `(client)` | Ready packet processed, `sid`/world set, queued writes flushed |
|
|
51
|
+
| `disconnect` | `(info: DisconnectInfo)` | Close cleanup completed; see [disconnects](CLIENT.md#disconnects) |
|
|
52
|
+
| `close` | Browser `(event, reason)` or Node `(code, reason)` | Raw close arguments, after `disconnect` |
|
|
53
|
+
| `packet` | `(packet)` | Decoded inbound packet retained, before it is applied to world/management state |
|
|
54
|
+
| `command` | `(command)` | Signed input sent; no server acknowledgment implied |
|
|
55
|
+
| `message` | `(message)` | Decoded outgoing message encoded/sent; no server acknowledgment implied |
|
|
56
|
+
| `bootstrap` | `(message)` | Initial outfit/bootstrap message sent by an owning client |
|
|
57
|
+
| `event` | `(packet)` | Inbound packet not handled by a more specific branch |
|
|
58
|
+
|
|
59
|
+
WebSocket errors are distinct from a later close. A `ready` event does not mean every state component is present. Raw
|
|
60
|
+
`packet` callbacks run before state application; read updated state from a specific update event instead.
|
|
61
|
+
|
|
62
|
+
## World updates
|
|
63
|
+
|
|
64
|
+
These events receive an update object with `.world` referencing the already-updated `WorldState`:
|
|
65
|
+
|
|
66
|
+
| Event | Callback arguments | Important fields |
|
|
67
|
+
|-----------------|--------------------|-------------------------------------------------------------------------------|
|
|
68
|
+
| `world` | `(update)` | `update.world` with metadata applied |
|
|
69
|
+
| `tiles` | `(update)` | `world`, `decoded`, `updates`, `errors` |
|
|
70
|
+
| `model` | `(update)` | `world`, `result` with model changes, side events, command number, timing |
|
|
71
|
+
| `comms-bubble` | `(update)` | `world`, `packet`, `bubble` |
|
|
72
|
+
| `world-removed` | See below | World removed from store |
|
|
73
|
+
| `ack` | `(ack)` | `{world, commandNumber}`; latest advancing nonnegative command acknowledgment |
|
|
74
|
+
|
|
75
|
+
The current implementation emits `world-removed` twice: first with the world update object, then with its raw packet.
|
|
76
|
+
Handle that duplication if subscribing directly. It does not imply two removals.
|
|
77
|
+
|
|
78
|
+
`model` fires after world/model application but before its side events are dispatched to inventory, panels, and other
|
|
79
|
+
caches. The corresponding acknowledgment is processed afterward too. Do not assume an inventory event embedded in the
|
|
80
|
+
same packet has already run inside a `model` listener.
|
|
81
|
+
|
|
82
|
+
## Player and interaction updates
|
|
83
|
+
|
|
84
|
+
| Event | Callback arguments | Cache/state |
|
|
85
|
+
|------------------|--------------------------------------|----------------------------------------------------------|
|
|
86
|
+
| `inventory` | `(inventory: InventoryState, world)` | `client.inventory.state()` updated |
|
|
87
|
+
| `tip-list` | `(menu: TipList, world)` | Frozen menu; empty options also emitted |
|
|
88
|
+
| `pui` | `(panel, world)` | `client.puiPanels`, keyed by entity |
|
|
89
|
+
| `comms` | `(panel, world)` | `client.commsPanels` and active panel |
|
|
90
|
+
| `side-event` | `(event, world)` | Remaining model side events; also receives raw tip lists |
|
|
91
|
+
| `tip_warn` | `(event, world)` | Warning retained in `client.warnings` |
|
|
92
|
+
| `sfx` | `(event, world)` | Effect retained in `client.effects` |
|
|
93
|
+
| `chat` | `(packet)` | Raw chat packet retained in `client.chat` |
|
|
94
|
+
| `motd` | `(packet)` | Raw MOTD packet retained in `client.motd` |
|
|
95
|
+
| `outfit` | `(packet)` | Outfit cached by protocol session ID |
|
|
96
|
+
| `cpu` | `(packet)` | `client.cpuLoad` set from packet |
|
|
97
|
+
| `scanner-result` | `(result, packet)` | `lastScannerResult` and history updated |
|
|
98
|
+
|
|
99
|
+
`tip-list` does not carry an authoritative target ID from the server. Its `.entity` is inferred from a pending menu
|
|
100
|
+
request, or `null` for unsolicited menus. [Context menus](MACHINES.md#context-menus-and-access) explains concurrency and
|
|
101
|
+
targeting.
|
|
102
|
+
|
|
103
|
+
`CommsEvent.messages` contains `{raw, text}` entries; `text` flattens supported rich-text content.
|
|
104
|
+
See [declarations](../src/index.d.ts) for full `InventoryState`, `TipList`, `PuiEvent`, `CommsEvent`, and
|
|
105
|
+
`ScannerResult` shapes.
|
|
106
|
+
|
|
107
|
+
## Management updates
|
|
108
|
+
|
|
109
|
+
| Event | Callback arguments | Meaning |
|
|
110
|
+
|-------------------------|-----------------------------------|-------------------------------------------------------------|
|
|
111
|
+
| `ship-config` | `(config: ShipConfig | null, packet)` | Config cache updated or cleared |
|
|
112
|
+
| `captain-subrank` | `(rank: CaptainSubrank | null, packet)` | Captain/cheat cache updated or cleared |
|
|
113
|
+
| `player-list` | `(list: ShipPlayerList | null, packet)` | Player-list changes merged, or cache cleared |
|
|
114
|
+
| `crew-log` | `(log: CrewLog | null, packet)` | Reset/amend/append applied, or log cleared |
|
|
115
|
+
| `demote` | `(packet)` | Permission-scoped caches cleared before this event |
|
|
116
|
+
| `starter-recovery` | `({success, failReason}, packet)` | Recovery response; `failReason` is string or `null` |
|
|
117
|
+
| `patron-perks-response` | `({failure}, packet)` | Patron operation response; applied patrons come from config |
|
|
118
|
+
| `session` | `(packet)` | Raw captain/session packet, after dedicated handling |
|
|
119
|
+
|
|
120
|
+
Demotion clears ship config, captain subrank, player list, and crew log. It emits `demote`, then the four cache events
|
|
121
|
+
with `null`, then the raw `session` event. Disconnect also clears these caches, but uses the disconnect events rather
|
|
122
|
+
than emitting each cache event.
|
|
123
|
+
|
|
124
|
+
Rename permission replies remain on `session`; the rename promise handles them internally. It does not expose a
|
|
125
|
+
dedicated commit-success event. Detailed operation contracts are in [management](MANAGEMENT.md).
|
|
126
|
+
|
|
127
|
+
## Errors
|
|
128
|
+
|
|
129
|
+
| Event | Callback arguments | Use |
|
|
130
|
+
|----------------|---------------------------------|-----------------------------------------------------------------------|
|
|
131
|
+
| `error` | `(error)` | Connection/frame errors and forwarded listener exceptions |
|
|
132
|
+
| `decode-error` | `(entry, error, packet, world)` | A retained processing/decoding error; context arguments may be absent |
|
|
133
|
+
|
|
134
|
+
`entry` includes `time`, `message`, `packetType`, and `world`. Inspect `client.debug.decodeErrors()` for recent retained
|
|
135
|
+
entries. Some decoders can retain partial state despite reporting an error; an empty/missing decoded component is not
|
|
136
|
+
proof it does not exist on the server.
|