dredless 0.2.0 → 0.3.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.
Files changed (80) hide show
  1. package/README.md +65 -48
  2. package/dist/browser/dredless.global.js +12047 -0
  3. package/dist/browser/dredless.global.js.map +7 -0
  4. package/docs/API_REFERENCE.md +2481 -2481
  5. package/docs/BROWSER_API.md +98 -98
  6. package/docs/NODE_API.md +359 -359
  7. package/package.json +40 -35
  8. package/src/browser-global.js +3 -0
  9. package/src/browser.d.ts +212 -212
  10. package/src/browser.js +133 -133
  11. package/src/client/core.js +1202 -1202
  12. package/src/client/domains/index.js +1818 -1818
  13. package/src/client/index.d.ts +2 -2
  14. package/src/client/index.js +1 -1
  15. package/src/compression/lz4.d.ts +1 -1
  16. package/src/compression/lz4.js +112 -112
  17. package/src/constants.js +74 -74
  18. package/src/crypto/chacha-native.d.ts +9 -9
  19. package/src/crypto/chacha-native.js +48 -48
  20. package/src/crypto/chacha.d.ts +25 -25
  21. package/src/crypto/chacha.js +274 -274
  22. package/src/events.js +39 -39
  23. package/src/index.d.ts +2277 -2277
  24. package/src/index.js +125 -125
  25. package/src/network/browser-session.js +291 -291
  26. package/src/network/connection.d.ts +1 -1
  27. package/src/network/connection.js +31 -31
  28. package/src/network/cookies.js +85 -85
  29. package/src/network/http.js +34 -34
  30. package/src/network/index.d.ts +4 -4
  31. package/src/network/index.js +4 -4
  32. package/src/network/servers.d.ts +7 -7
  33. package/src/network/servers.js +133 -133
  34. package/src/network/session.d.ts +7 -7
  35. package/src/network/session.js +320 -320
  36. package/src/network/ships.d.ts +12 -12
  37. package/src/network/ships.js +73 -73
  38. package/src/node.d.ts +2 -2
  39. package/src/node.js +8 -8
  40. package/src/protocol/codec/binary.d.ts +3 -3
  41. package/src/protocol/codec/binary.js +52 -52
  42. package/src/protocol/codec/msgpack.d.ts +3 -3
  43. package/src/protocol/codec/msgpack.js +555 -555
  44. package/src/protocol/inbound/frame.d.ts +1 -1
  45. package/src/protocol/inbound/frame.js +8 -8
  46. package/src/protocol/inbound/world-payload.d.ts +3 -3
  47. package/src/protocol/inbound/world-payload.js +21 -21
  48. package/src/protocol/index.d.ts +11 -11
  49. package/src/protocol/index.js +19 -19
  50. package/src/protocol/outbound/blueprint.d.ts +22 -22
  51. package/src/protocol/outbound/blueprint.js +36 -36
  52. package/src/protocol/outbound/commands.d.ts +3 -3
  53. package/src/protocol/outbound/commands.js +149 -149
  54. package/src/protocol/outbound/comms.d.ts +28 -28
  55. package/src/protocol/outbound/comms.js +40 -40
  56. package/src/protocol/outbound/inventory.d.ts +64 -64
  57. package/src/protocol/outbound/inventory.js +147 -147
  58. package/src/protocol/outbound/ship-management.d.ts +84 -84
  59. package/src/protocol/outbound/ship-management.js +221 -221
  60. package/src/protocol/outbound/sign.d.ts +18 -18
  61. package/src/protocol/outbound/sign.js +45 -45
  62. package/src/protocol/outbound/ui-config.d.ts +157 -157
  63. package/src/protocol/outbound/ui-config.js +578 -578
  64. package/src/runtime.js +36 -36
  65. package/src/state/generator-maze.d.ts +32 -32
  66. package/src/state/generator-maze.js +370 -370
  67. package/src/state/index.d.ts +3 -3
  68. package/src/state/index.js +3 -3
  69. package/src/state/items.js +20 -20
  70. package/src/state/model/decoder.js +1554 -1554
  71. package/src/state/model/index.d.ts +33 -33
  72. package/src/state/model/index.js +2 -2
  73. package/src/state/model/loader-config.js +1020 -1020
  74. package/src/state/model/reader.js +69 -69
  75. package/src/state/model/state.js +1257 -1257
  76. package/src/state/overworld.js +72 -72
  77. package/src/state/tilesets.js +53 -53
  78. package/src/state/world/index.d.ts +29 -29
  79. package/src/state/world/index.js +180 -180
  80. package/src/state/world/state.js +297 -297
@@ -1,320 +1,320 @@
1
- import {DEFAULT_BASE_URL, DEFAULT_NOTICE_VERSION, JOIN_USER_AGENT} from "../constants.js";
2
- import {asNumber, isNode, normalizeBaseUrl} from "../runtime.js";
3
- import {cookieName, cookiePrefix, readSetCookies, setCookieValues} from "./cookies.js";
4
- import {HttpClient} from "./http.js";
5
- import {fetchGameVersion, resolveServer, serverId} from "./servers.js";
6
- import {createInviteShipSpec, createShipSpec, fetchShipList, fetchShips, shipRef} from "./ships.js";
7
- import {Connection} from "./connection.js";
8
- import {DredlessClient} from "../client/index.js";
9
-
10
- export class Session {
11
- constructor(gameSession = null, gameVersion = null, baseUrl = DEFAULT_BASE_URL) {
12
- this.baseUrl = baseUrl;
13
- this.cookies = setCookieValues(this.baseUrl, {});
14
- this.gameVersion = gameVersion || null;
15
- this._noticeVersion = null;
16
- this.account = null;
17
- this.geoServer = null;
18
- this.upgraded = false;
19
- this.isRegistered = false;
20
- this.showAds = false;
21
- this.forceTutorial = false;
22
- this.ban = null;
23
- if (gameSession) this.cookies.set(cookieName(this.baseUrl, "game_session"), String(gameSession));
24
- }
25
-
26
- get gameSession() {
27
- return this.cookies.get(cookieName(this.baseUrl, "game_session")) || "";
28
- }
29
-
30
- get gameToken() {
31
- return this.cookies.get(cookieName(this.baseUrl, "game_token")) || "";
32
- }
33
-
34
- get noticeVersion() {
35
- return this._noticeVersion ?? this.cookies.get(cookieName(this.baseUrl, "notice_version")) ?? null;
36
- }
37
-
38
- set noticeVersion(value) {
39
- this._noticeVersion = value == null ? null : Number(value);
40
- const key = cookieName(this.baseUrl, "notice_version");
41
- if (value == null) this.cookies.delete(key);
42
- else this.cookies.set(key, String(value));
43
- }
44
-
45
- request(path, init = {}) {
46
- return new HttpClient({baseUrl: this.baseUrl, session: this}).request(path, init);
47
- }
48
-
49
- async fetchAccountStatus() {
50
- const response = await this.request("account/status", {
51
- method: "GET",
52
- mode: "cors",
53
- credentials: "include",
54
- referrer: `${this.baseUrl}/`,
55
- headers: statusHeaders(this.baseUrl)
56
- });
57
- if (!response.ok) throw new Error(`account/status failed: ${response.status} ${response.statusText}`);
58
- this.mergeSetCookies(response);
59
- const raw = await response.json();
60
- this.#readStatus(raw);
61
- return raw;
62
- }
63
-
64
- async fetchShips(server) {
65
- return fetchShips(this, server);
66
- }
67
-
68
- async fetchShipList(server) {
69
- return fetchShipList(this, server);
70
- }
71
-
72
- async joinShipConnection(server, ship = null) {
73
- return this.#startShipConnection(server, ship, true);
74
- }
75
-
76
- async startShipConnection(server, ship = null) {
77
- return this.#startShipConnection(server, ship, false);
78
- }
79
-
80
- async startNewShipConnection(server, name = "", color = "") {
81
- return this.#startShipConnection(server, createShipSpec(name, color), false);
82
- }
83
-
84
- async joinInviteConnection(server, code) {
85
- return this.#startShipConnection(server, createInviteShipSpec(code), false);
86
- }
87
-
88
- async joinShip(server, ship = null) {
89
- return readyClient(await this.joinShipConnection(server, ship));
90
- }
91
-
92
- async startShip(server, ship = null) {
93
- return readyClient(await this.startShipConnection(server, ship));
94
- }
95
-
96
- async startNewShip(server, name = "", color = "") {
97
- return readyClient(await this.startNewShipConnection(server, name, color));
98
- }
99
-
100
- async joinInvite(server, code) {
101
- return readyClient(await this.joinInviteConnection(server, code));
102
- }
103
-
104
- toJSON() {
105
- return {
106
- baseUrl: this.baseUrl,
107
- gameSession: this.gameSession,
108
- gameToken: this.gameToken,
109
- gameVersion: this.gameVersion,
110
- noticeVersion: this.noticeVersion,
111
- cookies: Object.fromEntries(this.cookies),
112
- account: this.account,
113
- geoServer: this.geoServer,
114
- upgraded: this.upgraded,
115
- isRegistered: this.isRegistered,
116
- showAds: this.showAds,
117
- forceTutorial: this.forceTutorial,
118
- ban: this.ban,
119
- };
120
- }
121
-
122
- async #startShipConnection(server, ship, neverLoad) {
123
- if (server == null) throw new Error("A server id or server object is required");
124
- const resolvedServer = await resolveServer(server, this.baseUrl);
125
- if (!resolvedServer) throw new Error(`Unable to resolve server ${serverId(server)}`);
126
- if (!this.gameVersion) this.gameVersion = await fetchGameVersion(this.baseUrl);
127
- if (this.noticeVersion == null) this.noticeVersion = DEFAULT_NOTICE_VERSION;
128
-
129
- const joinShip = ship == null ? createShipSpec("", "") : shipRef(ship);
130
- const response = await this.request("join", {
131
- method: "POST",
132
- mode: "cors",
133
- credentials: "include",
134
- referrer: `${this.baseUrl}/`,
135
- body: {
136
- join_info: {
137
- game_version: this.gameVersion,
138
- hide_badges: false,
139
- never_load: Boolean(neverLoad),
140
- server_id: resolvedServer.index,
141
- ship: joinShip
142
- }
143
- },
144
- headers: joinHeaders(this.baseUrl)
145
- });
146
- if (!response.ok) throw new Error(`Join request failed: ${await response.text()}`);
147
- this.mergeSetCookies(response, ["game_session", "anon_key", "game_token"]);
148
- const join = await response.json();
149
- if (join.reject != null) {
150
- const error = new Error(joinRejectMessage(join.reject, {
151
- neverLoad,
152
- ship: joinShip,
153
- server: resolvedServer
154
- }));
155
- error.code = join.reject;
156
- error.join = join;
157
- throw error;
158
- }
159
- if (join.okay !== true) throw new Error("Bad join result");
160
-
161
- const token = this.gameToken;
162
- if (!token) throw new Error("Join did not return a game_token cookie");
163
- return new Connection(this, token, join.net_port, Number.isInteger(join.server_id) ? join.server_id : resolvedServer.index, resolvedServer);
164
- }
165
-
166
- mergeSetCookies(response, names = ["game_session", "anon_key"]) {
167
- const prefix = cookiePrefix(this.baseUrl);
168
- for (const [name, value] of readSetCookies(response.headers, names, this.baseUrl)) {
169
- const cookieName = name.startsWith(prefix) ? name : `${prefix}${name}`;
170
- this.cookies.set(cookieName, value);
171
- }
172
- }
173
-
174
- #readStatus(raw) {
175
- const account = raw?.account && typeof raw.account === "object" ? raw.account : null;
176
- this.account = account ? {
177
- name: account.name || account.username || account.display_name || account.displayName || account.handle || "Unknown",
178
- color: asNumber(account.color, 0),
179
- game_rank: asNumber(account.game_rank, 0),
180
- user_badges: Array.isArray(account.user_badges) ? account.user_badges.filter((item) => typeof item === "string") : [],
181
- is_registered: Boolean(account.is_registered)
182
- } : null;
183
- this.geoServer = asNumber(raw?.geo_server ?? raw?.geoServer, null);
184
- this.upgraded = Boolean(raw?.upgraded ?? account?.upgraded);
185
- this.isRegistered = Boolean(account?.is_registered);
186
- this.showAds = Boolean(raw?.show_ads ?? raw?.showAds);
187
- this.forceTutorial = Boolean(raw?.force_tutorial ?? raw?.forceTutorial);
188
- this.ban = raw?.ban || null;
189
- }
190
- }
191
-
192
- export class AnonSession extends Session {
193
- constructor(gameSession = null, anonKey = null, gameVersion = null, baseUrl = DEFAULT_BASE_URL) {
194
- super(gameSession, gameVersion, baseUrl);
195
- if (anonKey) this.cookies.set(cookieName(this.baseUrl, "anon_key"), String(anonKey));
196
- }
197
-
198
- get anonKey() {
199
- return this.cookies.get(cookieName(this.baseUrl, "anon_key")) || "";
200
- }
201
-
202
- toJSON() {
203
- return {
204
- ...super.toJSON(),
205
- anonKey: this.anonKey
206
- };
207
- }
208
- }
209
-
210
- export async function createSession(noticeVersion = null, baseUrl = DEFAULT_BASE_URL) {
211
- const resolvedBaseUrl = normalizeBaseUrl(arguments.length >= 2 ? arguments[1] : baseUrl);
212
- const session = new Session(null, null, resolvedBaseUrl);
213
- session.noticeVersion = await internalNoticeVersion(noticeVersion, resolvedBaseUrl);
214
- await session.fetchAccountStatus();
215
- return session;
216
- }
217
-
218
- export async function createAnonToken(noticeVersion = null, baseUrl = DEFAULT_BASE_URL) {
219
- const resolvedBaseUrl = normalizeBaseUrl(arguments.length >= 2 ? arguments[1] : baseUrl);
220
- const session = new AnonSession(null, null, null, resolvedBaseUrl);
221
- session.noticeVersion = await internalNoticeVersion(noticeVersion, resolvedBaseUrl);
222
- const response = await session.request("account/login/anon", {
223
- method: "POST",
224
- mode: "cors",
225
- credentials: "include",
226
- referrer: `${session.baseUrl}/`,
227
- headers: anonHeaders(session.baseUrl)
228
- });
229
- if (response.status === 503) throw new Error("Anon token creation is rate limited by the server");
230
- if (!response.ok) throw new Error(`account/login/anon failed: ${response.status} ${response.statusText}`);
231
- session.mergeSetCookies(response, ["anon_key", "game_session"]);
232
- if (!session.anonKey) throw new Error("Unable to obtain anon_key");
233
- return session.anonKey;
234
- }
235
-
236
- export async function createAnonSession(anonKey = null, noticeVersion = null, baseUrl = DEFAULT_BASE_URL) {
237
- if (typeof noticeVersion === "string" && noticeVersion.includes("://") && (baseUrl == null || baseUrl === DEFAULT_BASE_URL)) {
238
- baseUrl = noticeVersion;
239
- noticeVersion = null;
240
- }
241
- const resolvedBaseUrl = normalizeBaseUrl(arguments.length >= 3 ? arguments[2] : baseUrl);
242
- const resolvedAnonKey = anonKey || await createAnonToken(noticeVersion, resolvedBaseUrl);
243
- const session = new AnonSession(null, resolvedAnonKey, null, resolvedBaseUrl);
244
- session.baseUrl = resolvedBaseUrl;
245
- session.noticeVersion = await internalNoticeVersion(noticeVersion, resolvedBaseUrl);
246
- await session.fetchAccountStatus();
247
- return session;
248
- }
249
-
250
- async function readyClient(connection) {
251
- const client = new DredlessClient(connection);
252
- await client.whenReady();
253
- return client;
254
- }
255
-
256
- async function internalNoticeVersion(value, baseUrl = DEFAULT_BASE_URL) {
257
- if (value != null) return value;
258
- try {
259
- const {fetchNoticeVersion} = await import("./servers.js");
260
- return await fetchNoticeVersion(baseUrl);
261
- } catch (_) {
262
- return DEFAULT_NOTICE_VERSION;
263
- }
264
- }
265
-
266
- function statusHeaders(baseUrl) {
267
- const headers = {accept: "*/*"};
268
- if (isNode()) {
269
- Object.assign(headers, {
270
- "user-agent": JOIN_USER_AGENT,
271
- "accept-language": "en-US,en;q=0.9",
272
- referer: `${normalizeBaseUrl(baseUrl)}/`,
273
- dnt: "1",
274
- "sec-gpc": "1",
275
- "sec-fetch-dest": "empty",
276
- "sec-fetch-mode": "cors",
277
- "sec-fetch-site": "same-origin"
278
- });
279
- }
280
- return headers;
281
- }
282
-
283
- function anonHeaders(baseUrl) {
284
- const origin = normalizeBaseUrl(baseUrl);
285
- return {
286
- accept: "*/*",
287
- origin,
288
- referer: `${origin}/`
289
- };
290
- }
291
-
292
- function joinHeaders(baseUrl) {
293
- const headers = {
294
- accept: "*/*",
295
- "accept-language": "en-US,en;q=0.9",
296
- "content-type": "application/json",
297
- origin: normalizeBaseUrl(baseUrl),
298
- referer: `${normalizeBaseUrl(baseUrl)}/`,
299
- pragma: "no-cache",
300
- "cache-control": "no-cache",
301
- dnt: "1",
302
- "sec-gpc": "1",
303
- "sec-fetch-dest": "empty",
304
- "sec-fetch-mode": "cors",
305
- "sec-fetch-site": "same-origin",
306
- priority: "u=0"
307
- };
308
- if (isNode()) headers["user-agent"] = JOIN_USER_AGENT;
309
- return headers;
310
- }
311
-
312
- function joinRejectMessage(code, {neverLoad, ship, server} = {}) {
313
- if (code === 4007 && neverLoad) {
314
- const name = ship?.name ? ` "${ship.name}"` : "";
315
- const id = ship?.id != null ? ` (${ship.id})` : "";
316
- const serverText = server?.description ? ` on ${server.description}` : "";
317
- return `join rejected: 4007. ${name}${id}${serverText} could not be joined with never_load=true. Use session.startShip(server, ship) instead of session.joinShip(server, ship) when loading or starting a saved ship.`;
318
- }
319
- return `join rejected: ${code}`;
320
- }
1
+ import {DEFAULT_BASE_URL, DEFAULT_NOTICE_VERSION, JOIN_USER_AGENT} from "../constants.js";
2
+ import {asNumber, isNode, normalizeBaseUrl} from "../runtime.js";
3
+ import {cookieName, cookiePrefix, readSetCookies, setCookieValues} from "./cookies.js";
4
+ import {HttpClient} from "./http.js";
5
+ import {fetchGameVersion, resolveServer, serverId} from "./servers.js";
6
+ import {createInviteShipSpec, createShipSpec, fetchShipList, fetchShips, shipRef} from "./ships.js";
7
+ import {Connection} from "./connection.js";
8
+ import {DredlessClient} from "../client/index.js";
9
+
10
+ export class Session {
11
+ constructor(gameSession = null, gameVersion = null, baseUrl = DEFAULT_BASE_URL) {
12
+ this.baseUrl = baseUrl;
13
+ this.cookies = setCookieValues(this.baseUrl, {});
14
+ this.gameVersion = gameVersion || null;
15
+ this._noticeVersion = null;
16
+ this.account = null;
17
+ this.geoServer = null;
18
+ this.upgraded = false;
19
+ this.isRegistered = false;
20
+ this.showAds = false;
21
+ this.forceTutorial = false;
22
+ this.ban = null;
23
+ if (gameSession) this.cookies.set(cookieName(this.baseUrl, "game_session"), String(gameSession));
24
+ }
25
+
26
+ get gameSession() {
27
+ return this.cookies.get(cookieName(this.baseUrl, "game_session")) || "";
28
+ }
29
+
30
+ get gameToken() {
31
+ return this.cookies.get(cookieName(this.baseUrl, "game_token")) || "";
32
+ }
33
+
34
+ get noticeVersion() {
35
+ return this._noticeVersion ?? this.cookies.get(cookieName(this.baseUrl, "notice_version")) ?? null;
36
+ }
37
+
38
+ set noticeVersion(value) {
39
+ this._noticeVersion = value == null ? null : Number(value);
40
+ const key = cookieName(this.baseUrl, "notice_version");
41
+ if (value == null) this.cookies.delete(key);
42
+ else this.cookies.set(key, String(value));
43
+ }
44
+
45
+ request(path, init = {}) {
46
+ return new HttpClient({baseUrl: this.baseUrl, session: this}).request(path, init);
47
+ }
48
+
49
+ async fetchAccountStatus() {
50
+ const response = await this.request("account/status", {
51
+ method: "GET",
52
+ mode: "cors",
53
+ credentials: "include",
54
+ referrer: `${this.baseUrl}/`,
55
+ headers: statusHeaders(this.baseUrl)
56
+ });
57
+ if (!response.ok) throw new Error(`account/status failed: ${response.status} ${response.statusText}`);
58
+ this.mergeSetCookies(response);
59
+ const raw = await response.json();
60
+ this.#readStatus(raw);
61
+ return raw;
62
+ }
63
+
64
+ async fetchShips(server) {
65
+ return fetchShips(this, server);
66
+ }
67
+
68
+ async fetchShipList(server) {
69
+ return fetchShipList(this, server);
70
+ }
71
+
72
+ async joinShipConnection(server, ship = null) {
73
+ return this.#startShipConnection(server, ship, true);
74
+ }
75
+
76
+ async startShipConnection(server, ship = null) {
77
+ return this.#startShipConnection(server, ship, false);
78
+ }
79
+
80
+ async startNewShipConnection(server, name = "", color = "") {
81
+ return this.#startShipConnection(server, createShipSpec(name, color), false);
82
+ }
83
+
84
+ async joinInviteConnection(server, code) {
85
+ return this.#startShipConnection(server, createInviteShipSpec(code), false);
86
+ }
87
+
88
+ async joinShip(server, ship = null) {
89
+ return readyClient(await this.joinShipConnection(server, ship));
90
+ }
91
+
92
+ async startShip(server, ship = null) {
93
+ return readyClient(await this.startShipConnection(server, ship));
94
+ }
95
+
96
+ async startNewShip(server, name = "", color = "") {
97
+ return readyClient(await this.startNewShipConnection(server, name, color));
98
+ }
99
+
100
+ async joinInvite(server, code) {
101
+ return readyClient(await this.joinInviteConnection(server, code));
102
+ }
103
+
104
+ toJSON() {
105
+ return {
106
+ baseUrl: this.baseUrl,
107
+ gameSession: this.gameSession,
108
+ gameToken: this.gameToken,
109
+ gameVersion: this.gameVersion,
110
+ noticeVersion: this.noticeVersion,
111
+ cookies: Object.fromEntries(this.cookies),
112
+ account: this.account,
113
+ geoServer: this.geoServer,
114
+ upgraded: this.upgraded,
115
+ isRegistered: this.isRegistered,
116
+ showAds: this.showAds,
117
+ forceTutorial: this.forceTutorial,
118
+ ban: this.ban,
119
+ };
120
+ }
121
+
122
+ async #startShipConnection(server, ship, neverLoad) {
123
+ if (server == null) throw new Error("A server id or server object is required");
124
+ const resolvedServer = await resolveServer(server, this.baseUrl);
125
+ if (!resolvedServer) throw new Error(`Unable to resolve server ${serverId(server)}`);
126
+ if (!this.gameVersion) this.gameVersion = await fetchGameVersion(this.baseUrl);
127
+ if (this.noticeVersion == null) this.noticeVersion = DEFAULT_NOTICE_VERSION;
128
+
129
+ const joinShip = ship == null ? createShipSpec("", "") : shipRef(ship);
130
+ const response = await this.request("join", {
131
+ method: "POST",
132
+ mode: "cors",
133
+ credentials: "include",
134
+ referrer: `${this.baseUrl}/`,
135
+ body: {
136
+ join_info: {
137
+ game_version: this.gameVersion,
138
+ hide_badges: false,
139
+ never_load: Boolean(neverLoad),
140
+ server_id: resolvedServer.index,
141
+ ship: joinShip
142
+ }
143
+ },
144
+ headers: joinHeaders(this.baseUrl)
145
+ });
146
+ if (!response.ok) throw new Error(`Join request failed: ${await response.text()}`);
147
+ this.mergeSetCookies(response, ["game_session", "anon_key", "game_token"]);
148
+ const join = await response.json();
149
+ if (join.reject != null) {
150
+ const error = new Error(joinRejectMessage(join.reject, {
151
+ neverLoad,
152
+ ship: joinShip,
153
+ server: resolvedServer
154
+ }));
155
+ error.code = join.reject;
156
+ error.join = join;
157
+ throw error;
158
+ }
159
+ if (join.okay !== true) throw new Error("Bad join result");
160
+
161
+ const token = this.gameToken;
162
+ if (!token) throw new Error("Join did not return a game_token cookie");
163
+ return new Connection(this, token, join.net_port, Number.isInteger(join.server_id) ? join.server_id : resolvedServer.index, resolvedServer);
164
+ }
165
+
166
+ mergeSetCookies(response, names = ["game_session", "anon_key"]) {
167
+ const prefix = cookiePrefix(this.baseUrl);
168
+ for (const [name, value] of readSetCookies(response.headers, names, this.baseUrl)) {
169
+ const cookieName = name.startsWith(prefix) ? name : `${prefix}${name}`;
170
+ this.cookies.set(cookieName, value);
171
+ }
172
+ }
173
+
174
+ #readStatus(raw) {
175
+ const account = raw?.account && typeof raw.account === "object" ? raw.account : null;
176
+ this.account = account ? {
177
+ name: account.name || account.username || account.display_name || account.displayName || account.handle || "Unknown",
178
+ color: asNumber(account.color, 0),
179
+ game_rank: asNumber(account.game_rank, 0),
180
+ user_badges: Array.isArray(account.user_badges) ? account.user_badges.filter((item) => typeof item === "string") : [],
181
+ is_registered: Boolean(account.is_registered)
182
+ } : null;
183
+ this.geoServer = asNumber(raw?.geo_server ?? raw?.geoServer, null);
184
+ this.upgraded = Boolean(raw?.upgraded ?? account?.upgraded);
185
+ this.isRegistered = Boolean(account?.is_registered);
186
+ this.showAds = Boolean(raw?.show_ads ?? raw?.showAds);
187
+ this.forceTutorial = Boolean(raw?.force_tutorial ?? raw?.forceTutorial);
188
+ this.ban = raw?.ban || null;
189
+ }
190
+ }
191
+
192
+ export class AnonSession extends Session {
193
+ constructor(gameSession = null, anonKey = null, gameVersion = null, baseUrl = DEFAULT_BASE_URL) {
194
+ super(gameSession, gameVersion, baseUrl);
195
+ if (anonKey) this.cookies.set(cookieName(this.baseUrl, "anon_key"), String(anonKey));
196
+ }
197
+
198
+ get anonKey() {
199
+ return this.cookies.get(cookieName(this.baseUrl, "anon_key")) || "";
200
+ }
201
+
202
+ toJSON() {
203
+ return {
204
+ ...super.toJSON(),
205
+ anonKey: this.anonKey
206
+ };
207
+ }
208
+ }
209
+
210
+ export async function createSession(noticeVersion = null, baseUrl = DEFAULT_BASE_URL) {
211
+ const resolvedBaseUrl = normalizeBaseUrl(arguments.length >= 2 ? arguments[1] : baseUrl);
212
+ const session = new Session(null, null, resolvedBaseUrl);
213
+ session.noticeVersion = await internalNoticeVersion(noticeVersion, resolvedBaseUrl);
214
+ await session.fetchAccountStatus();
215
+ return session;
216
+ }
217
+
218
+ export async function createAnonToken(noticeVersion = null, baseUrl = DEFAULT_BASE_URL) {
219
+ const resolvedBaseUrl = normalizeBaseUrl(arguments.length >= 2 ? arguments[1] : baseUrl);
220
+ const session = new AnonSession(null, null, null, resolvedBaseUrl);
221
+ session.noticeVersion = await internalNoticeVersion(noticeVersion, resolvedBaseUrl);
222
+ const response = await session.request("account/login/anon", {
223
+ method: "POST",
224
+ mode: "cors",
225
+ credentials: "include",
226
+ referrer: `${session.baseUrl}/`,
227
+ headers: anonHeaders(session.baseUrl)
228
+ });
229
+ if (response.status === 503) throw new Error("Anon token creation is rate limited by the server");
230
+ if (!response.ok) throw new Error(`account/login/anon failed: ${response.status} ${response.statusText}`);
231
+ session.mergeSetCookies(response, ["anon_key", "game_session"]);
232
+ if (!session.anonKey) throw new Error("Unable to obtain anon_key");
233
+ return session.anonKey;
234
+ }
235
+
236
+ export async function createAnonSession(anonKey = null, noticeVersion = null, baseUrl = DEFAULT_BASE_URL) {
237
+ if (typeof noticeVersion === "string" && noticeVersion.includes("://") && (baseUrl == null || baseUrl === DEFAULT_BASE_URL)) {
238
+ baseUrl = noticeVersion;
239
+ noticeVersion = null;
240
+ }
241
+ const resolvedBaseUrl = normalizeBaseUrl(arguments.length >= 3 ? arguments[2] : baseUrl);
242
+ const resolvedAnonKey = anonKey || await createAnonToken(noticeVersion, resolvedBaseUrl);
243
+ const session = new AnonSession(null, resolvedAnonKey, null, resolvedBaseUrl);
244
+ session.baseUrl = resolvedBaseUrl;
245
+ session.noticeVersion = await internalNoticeVersion(noticeVersion, resolvedBaseUrl);
246
+ await session.fetchAccountStatus();
247
+ return session;
248
+ }
249
+
250
+ async function readyClient(connection) {
251
+ const client = new DredlessClient(connection);
252
+ await client.whenReady();
253
+ return client;
254
+ }
255
+
256
+ async function internalNoticeVersion(value, baseUrl = DEFAULT_BASE_URL) {
257
+ if (value != null) return value;
258
+ try {
259
+ const {fetchNoticeVersion} = await import("./servers.js");
260
+ return await fetchNoticeVersion(baseUrl);
261
+ } catch (_) {
262
+ return DEFAULT_NOTICE_VERSION;
263
+ }
264
+ }
265
+
266
+ function statusHeaders(baseUrl) {
267
+ const headers = {accept: "*/*"};
268
+ if (isNode()) {
269
+ Object.assign(headers, {
270
+ "user-agent": JOIN_USER_AGENT,
271
+ "accept-language": "en-US,en;q=0.9",
272
+ referer: `${normalizeBaseUrl(baseUrl)}/`,
273
+ dnt: "1",
274
+ "sec-gpc": "1",
275
+ "sec-fetch-dest": "empty",
276
+ "sec-fetch-mode": "cors",
277
+ "sec-fetch-site": "same-origin"
278
+ });
279
+ }
280
+ return headers;
281
+ }
282
+
283
+ function anonHeaders(baseUrl) {
284
+ const origin = normalizeBaseUrl(baseUrl);
285
+ return {
286
+ accept: "*/*",
287
+ origin,
288
+ referer: `${origin}/`
289
+ };
290
+ }
291
+
292
+ function joinHeaders(baseUrl) {
293
+ const headers = {
294
+ accept: "*/*",
295
+ "accept-language": "en-US,en;q=0.9",
296
+ "content-type": "application/json",
297
+ origin: normalizeBaseUrl(baseUrl),
298
+ referer: `${normalizeBaseUrl(baseUrl)}/`,
299
+ pragma: "no-cache",
300
+ "cache-control": "no-cache",
301
+ dnt: "1",
302
+ "sec-gpc": "1",
303
+ "sec-fetch-dest": "empty",
304
+ "sec-fetch-mode": "cors",
305
+ "sec-fetch-site": "same-origin",
306
+ priority: "u=0"
307
+ };
308
+ if (isNode()) headers["user-agent"] = JOIN_USER_AGENT;
309
+ return headers;
310
+ }
311
+
312
+ function joinRejectMessage(code, {neverLoad, ship, server} = {}) {
313
+ if (code === 4007 && neverLoad) {
314
+ const name = ship?.name ? ` "${ship.name}"` : "";
315
+ const id = ship?.id != null ? ` (${ship.id})` : "";
316
+ const serverText = server?.description ? ` on ${server.description}` : "";
317
+ return `join rejected: 4007. ${name}${id}${serverText} could not be joined with never_load=true. Use session.startShip(server, ship) instead of session.joinShip(server, ship) when loading or starting a saved ship.`;
318
+ }
319
+ return `join rejected: ${code}`;
320
+ }
@@ -1,12 +1,12 @@
1
- export {
2
- fetchShips,
3
- fetchShipList,
4
- type Ship,
5
- type ShipList,
6
- type ShipRef,
7
- type ShipSpec
8
- } from "../index.js";
9
-
10
- export function createShipSpec(name?: string, color?: string): ShipSpec;
11
-
12
- export function createInviteShipSpec(code: string): ShipSpec;
1
+ export {
2
+ fetchShips,
3
+ fetchShipList,
4
+ type Ship,
5
+ type ShipList,
6
+ type ShipRef,
7
+ type ShipSpec
8
+ } from "../index.js";
9
+
10
+ export function createShipSpec(name?: string, color?: string): ShipSpec;
11
+
12
+ export function createInviteShipSpec(code: string): ShipSpec;