@memizy/host-sdk 1.0.0-rc.5 → 1.0.0-rc.6
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/CHANGELOG.md +20 -0
- package/LICENSE +21 -0
- package/README.md +39 -5
- package/dist/frame.cjs +1 -1
- package/dist/frame.js +15 -14
- package/dist/index.cjs +1 -1
- package/dist/index.js +10 -10
- package/dist/relay/host.cjs +1 -1
- package/dist/relay/host.js +244 -72
- package/dist/relay/messages.cjs +1 -1
- package/dist/relay/messages.js +11 -6
- package/dist/relay/player.cjs +1 -1
- package/dist/relay/player.js +76 -23
- package/dist/session.cjs +1 -1
- package/dist/session.js +150 -132
- package/dist/src/frame.d.ts +7 -2
- package/dist/src/index.d.ts +15 -11
- package/dist/src/learning.d.ts +6 -2
- package/dist/src/relay/host.d.ts +76 -14
- package/dist/src/relay/messages.d.ts +42 -2
- package/dist/src/relay/player.d.ts +34 -5
- package/dist/src/session.d.ts +38 -23
- package/dist/src/storage.d.ts +3 -0
- package/dist/src/version.d.ts +2 -0
- package/dist/storage.cjs +1 -1
- package/dist/storage.js +3 -0
- package/dist/version.cjs +1 -0
- package/dist/version.js +4 -0
- package/package.json +10 -7
package/dist/relay/host.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { RelaySocket as e, relayWebSocketUrl as t } from "./socket.js";
|
|
2
|
-
import { blobToJson as n, serializeError as r } from "./messages.js";
|
|
3
|
-
import { HOST_API_METHODS as
|
|
2
|
+
import { blobToJson as n, serializeError as r, unknownMethod as i } from "./messages.js";
|
|
3
|
+
import { HOST_API_METHODS as a, PLUGIN_API_METHODS as o, RELAY_PROTOCOL as s } from "@memizy/protocol";
|
|
4
|
+
import { RELAY_LIMITS as c, jsonByteSize as l } from "@memizy/protocol/internal";
|
|
4
5
|
//#region src/relay/host.ts
|
|
5
|
-
var
|
|
6
|
+
var u = new Set(a), d = class a {
|
|
6
7
|
pin;
|
|
7
8
|
hostToken;
|
|
8
9
|
players = [];
|
|
@@ -23,12 +24,15 @@ var s = new Set(i), c = class i {
|
|
|
23
24
|
hosting = !1;
|
|
24
25
|
hostingWaiters = [];
|
|
25
26
|
stopSession = null;
|
|
27
|
+
seatOf = /* @__PURE__ */ new Map();
|
|
28
|
+
seatsMode = !1;
|
|
29
|
+
appVersions = /* @__PURE__ */ new Map();
|
|
26
30
|
constructor(n, r) {
|
|
27
31
|
this.options = n, this.pin = r.pin, this.hostToken = r.hostToken, this.socket = new e({
|
|
28
32
|
url: t(n.serverUrl),
|
|
29
33
|
attach: () => ({
|
|
30
34
|
t: "host",
|
|
31
|
-
protocol:
|
|
35
|
+
protocol: s,
|
|
32
36
|
pin: this.pin,
|
|
33
37
|
hostToken: this.hostToken
|
|
34
38
|
}),
|
|
@@ -41,10 +45,10 @@ var s = new Set(i), c = class i {
|
|
|
41
45
|
static async create(e) {
|
|
42
46
|
let t = await (e.fetch ?? fetch)(new URL("/api/rooms", e.serverUrl), { method: "POST" });
|
|
43
47
|
if (!t.ok) throw Error(`Could not create a room (${t.status}): ${await t.text()}`);
|
|
44
|
-
return new
|
|
48
|
+
return new a(e, await t.json());
|
|
45
49
|
}
|
|
46
|
-
static
|
|
47
|
-
return new
|
|
50
|
+
static rejoin(e, t) {
|
|
51
|
+
return new a(e, t);
|
|
48
52
|
}
|
|
49
53
|
whenHosting() {
|
|
50
54
|
return this.hosting ? Promise.resolve() : new Promise((e) => this.hostingWaiters.push(e));
|
|
@@ -59,7 +63,7 @@ var s = new Set(i), c = class i {
|
|
|
59
63
|
for (let t of this.listeners) t(e);
|
|
60
64
|
}
|
|
61
65
|
async uploadBundle(e, t) {
|
|
62
|
-
let n = await (this.options.fetch ?? fetch)(new URL(`/api/rooms/${this.pin}/bundle`, this.options.serverUrl), {
|
|
66
|
+
let n = t.publicSet, r = await (this.options.fetch ?? fetch)(new URL(`/api/rooms/${this.pin}/bundle`, this.options.serverUrl), {
|
|
63
67
|
method: "PUT",
|
|
64
68
|
headers: {
|
|
65
69
|
Authorization: `Bearer ${this.hostToken}`,
|
|
@@ -67,11 +71,11 @@ var s = new Set(i), c = class i {
|
|
|
67
71
|
},
|
|
68
72
|
body: JSON.stringify({
|
|
69
73
|
pluginHtml: e,
|
|
70
|
-
set:
|
|
74
|
+
set: n
|
|
71
75
|
})
|
|
72
76
|
});
|
|
73
|
-
if (!
|
|
74
|
-
return this.bundleVersion = (await
|
|
77
|
+
if (!r.ok) throw Error(`Could not upload the game (${r.status}): ${await r.text()}`);
|
|
78
|
+
return this.bundleVersion = (await r.json()).version, this.bundleVersion;
|
|
75
79
|
}
|
|
76
80
|
setOpen(e) {
|
|
77
81
|
this.socket.send({
|
|
@@ -100,24 +104,135 @@ var s = new Set(i), c = class i {
|
|
|
100
104
|
state: this.state
|
|
101
105
|
});
|
|
102
106
|
}
|
|
103
|
-
attach(e) {
|
|
104
|
-
this.detach(), this.session = e;
|
|
105
|
-
let
|
|
106
|
-
for (let
|
|
107
|
-
for (let
|
|
108
|
-
|
|
107
|
+
attach(e, t = {}) {
|
|
108
|
+
this.detach(), this.session = e, this.seatsMode = t.seats === !0;
|
|
109
|
+
let n = (t) => e.players.some((e) => e.id === t && !e.isHost), r = (e) => this.players.some((t) => t.id === e);
|
|
110
|
+
if (this.seatsMode) for (let [e, i] of Object.entries(t.taken ?? {})) r(e) && n(i) && this.relayIdOf(i) === null && this.seatOf.set(e, i);
|
|
111
|
+
else for (let e of this.players) n(e.id) && this.seatOf.set(e.id, e.id);
|
|
112
|
+
for (let t of e.players) {
|
|
113
|
+
if (t.isHost) continue;
|
|
114
|
+
let n = this.relayIdOf(t.id);
|
|
115
|
+
this.players.find((e) => e.id === n)?.connected || e.setConnected(t.id, !1);
|
|
116
|
+
}
|
|
117
|
+
for (let e of this.seatOf.values()) this.connectRemote(e);
|
|
118
|
+
if (this.stopSession = e.on((e) => this.onSessionEvent(e)), this.setState({
|
|
109
119
|
phase: "loading",
|
|
110
120
|
appName: e.plugin.manifest.appName,
|
|
111
|
-
players: e.players,
|
|
121
|
+
players: [...e.players],
|
|
112
122
|
authorityConnected: !0,
|
|
113
123
|
result: void 0,
|
|
114
|
-
countdown: void 0
|
|
115
|
-
|
|
124
|
+
countdown: void 0,
|
|
125
|
+
...this.seatState()
|
|
126
|
+
}), this.seatsMode) for (let e of this.players) e.connected && this.sendSeat(e.id);
|
|
116
127
|
}
|
|
117
128
|
detach() {
|
|
118
129
|
this.stopSession?.(), this.stopSession = null, this.session = null;
|
|
119
130
|
for (let [e, t] of this.channels) this.resetChannel(e, t);
|
|
120
|
-
this.
|
|
131
|
+
this.seatOf.clear(), this.seatsMode && (this.seatsMode = !1, this.state = {
|
|
132
|
+
...this.state,
|
|
133
|
+
seats: void 0,
|
|
134
|
+
newPlayers: void 0
|
|
135
|
+
}, this.broadcast({
|
|
136
|
+
k: "state",
|
|
137
|
+
state: this.state
|
|
138
|
+
})), this.broadcast({ k: "unmount" });
|
|
139
|
+
}
|
|
140
|
+
get seats() {
|
|
141
|
+
let e = this.session;
|
|
142
|
+
return !this.seatsMode || !e ? [] : e.players.filter((e) => !e.isHost).map((e) => ({
|
|
143
|
+
id: e.id,
|
|
144
|
+
name: e.name,
|
|
145
|
+
roomPlayerId: this.relayIdOf(e.id)
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
get seatAssignments() {
|
|
149
|
+
return Object.fromEntries(this.seatOf);
|
|
150
|
+
}
|
|
151
|
+
assignSeat(e, t) {
|
|
152
|
+
let n = this.session;
|
|
153
|
+
if (!this.seatsMode || !n) throw Error("Places can be assigned only in a continued game.");
|
|
154
|
+
if (!this.players.some((t) => t.id === e)) throw Error(`Unknown room player "${e}".`);
|
|
155
|
+
if (t !== null && !n.players.some((e) => e.id === t && !e.isHost)) throw Error(`Unknown place "${t}".`);
|
|
156
|
+
if ((this.seatOf.get(e) ?? null) !== t) {
|
|
157
|
+
if (this.unseat(e), t !== null) {
|
|
158
|
+
let n = this.relayIdOf(t);
|
|
159
|
+
n !== null && this.unseat(n), this.seat(e, t);
|
|
160
|
+
}
|
|
161
|
+
this.seatsChanged();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
appVersionOf(e) {
|
|
165
|
+
return this.appVersions.get(e) ?? null;
|
|
166
|
+
}
|
|
167
|
+
relayIdOf(e) {
|
|
168
|
+
for (let [t, n] of this.seatOf) if (n === e) return t;
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
seatState() {
|
|
172
|
+
let e = this.session;
|
|
173
|
+
if (!this.seatsMode || !e) return {
|
|
174
|
+
seats: void 0,
|
|
175
|
+
newPlayers: void 0
|
|
176
|
+
};
|
|
177
|
+
let t = e.plugin.runtime.multiplayer;
|
|
178
|
+
return {
|
|
179
|
+
seats: this.seats.map((e) => ({
|
|
180
|
+
id: e.id,
|
|
181
|
+
name: e.name,
|
|
182
|
+
taken: e.roomPlayerId !== null
|
|
183
|
+
})),
|
|
184
|
+
newPlayers: !e.ended && !!t?.lateJoin && e.players.length < t.players.max
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
seatsChanged() {
|
|
188
|
+
this.seatsMode && (this.setState(this.seatState()), this.emit({
|
|
189
|
+
type: "seats",
|
|
190
|
+
seats: this.seats
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
sendSeat(e) {
|
|
194
|
+
let t = this.seatOf.get(e) ?? null, n = t === null ? void 0 : this.session?.players.find((e) => e.id === t)?.name;
|
|
195
|
+
this.sendTo(e, {
|
|
196
|
+
k: "seat",
|
|
197
|
+
seat: t,
|
|
198
|
+
...n !== void 0 && { name: n }
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
seat(e, t) {
|
|
202
|
+
this.seatOf.set(e, t);
|
|
203
|
+
let n = this.players.find((t) => t.id === e)?.connected ?? !1;
|
|
204
|
+
this.session?.setConnected(t, n), n && this.connectRemote(t), this.sendSeat(e);
|
|
205
|
+
}
|
|
206
|
+
unseat(e) {
|
|
207
|
+
let t = this.seatOf.get(e);
|
|
208
|
+
if (t === void 0) return;
|
|
209
|
+
this.seatOf.delete(e);
|
|
210
|
+
let n = this.channels.get(t);
|
|
211
|
+
n && this.resetChannel(t, n), this.channels.delete(t), this.session?.disconnect(t), this.session?.setConnected(t, !1), this.sendTo(e, { k: "unmount" }), this.sendSeat(e);
|
|
212
|
+
}
|
|
213
|
+
claim(e, t) {
|
|
214
|
+
let n = this.session, r = this.players.find((t) => t.id === e);
|
|
215
|
+
if (!this.seatsMode || !n || n.ended || !r || this.seatOf.has(e)) {
|
|
216
|
+
this.sendSeat(e);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (t === null) {
|
|
220
|
+
try {
|
|
221
|
+
n.addPlayer({
|
|
222
|
+
id: e,
|
|
223
|
+
name: r.name
|
|
224
|
+
});
|
|
225
|
+
} catch {
|
|
226
|
+
this.sendSeat(e);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
this.seat(e, e);
|
|
230
|
+
} else if (typeof t == "string" && n.players.some((e) => e.id === t && !e.isHost) && this.relayIdOf(t) === null) this.seat(e, t);
|
|
231
|
+
else {
|
|
232
|
+
this.sendSeat(e);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
this.seatsChanged();
|
|
121
236
|
}
|
|
122
237
|
onSessionEvent(e) {
|
|
123
238
|
switch (e.type) {
|
|
@@ -134,12 +249,18 @@ var s = new Set(i), c = class i {
|
|
|
134
249
|
});
|
|
135
250
|
break;
|
|
136
251
|
case "players":
|
|
137
|
-
this.setState({
|
|
252
|
+
this.setState({
|
|
253
|
+
players: e.players,
|
|
254
|
+
...this.seatState()
|
|
255
|
+
}), this.seatsMode && this.emit({
|
|
256
|
+
type: "seats",
|
|
257
|
+
seats: this.seats
|
|
258
|
+
});
|
|
138
259
|
break;
|
|
139
260
|
case "authority":
|
|
140
261
|
this.setState({ authorityConnected: e.connected });
|
|
141
262
|
break;
|
|
142
|
-
case "
|
|
263
|
+
case "result": this.setState({
|
|
143
264
|
phase: "ended",
|
|
144
265
|
result: e.result
|
|
145
266
|
});
|
|
@@ -165,34 +286,36 @@ var s = new Set(i), c = class i {
|
|
|
165
286
|
});
|
|
166
287
|
}
|
|
167
288
|
sendMount(e, t) {
|
|
168
|
-
this.
|
|
289
|
+
this.sendToSeat(e, {
|
|
169
290
|
k: "mount",
|
|
170
291
|
gen: t,
|
|
171
292
|
bundle: this.bundleVersion,
|
|
172
|
-
locale: this.session?.config.locale ?? "
|
|
293
|
+
locale: this.session?.config.locale ?? "en",
|
|
294
|
+
v: 1
|
|
173
295
|
});
|
|
174
296
|
}
|
|
175
297
|
remotePlugin(e, t) {
|
|
176
298
|
let n = {};
|
|
177
|
-
for (let r of
|
|
299
|
+
for (let r of o) n[r] = (...n) => this.callRemote(e, t, r, n);
|
|
178
300
|
return n;
|
|
179
301
|
}
|
|
180
302
|
callRemote(e, t, n, r) {
|
|
181
303
|
let i = this.channels.get(e);
|
|
182
304
|
if (!i || i.gen !== t) return Promise.reject(/* @__PURE__ */ Error("[SESSION_ENDED] The player instance was replaced."));
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
305
|
+
let a = this.relayIdOf(e);
|
|
306
|
+
if (!this.players.find((e) => e.id === a)?.connected) return Promise.resolve();
|
|
307
|
+
let o = ++this.callCounter;
|
|
308
|
+
return new Promise((a, s) => {
|
|
186
309
|
let c = setTimeout(() => {
|
|
187
|
-
i.calls.delete(
|
|
310
|
+
i.calls.delete(o), s(/* @__PURE__ */ Error(`[INTERNAL_ERROR] ${n} on ${e} timed out.`));
|
|
188
311
|
}, this.options.callTimeoutMs ?? 15e3);
|
|
189
|
-
i.calls.set(
|
|
190
|
-
resolve:
|
|
312
|
+
i.calls.set(o, {
|
|
313
|
+
resolve: a,
|
|
191
314
|
reject: s,
|
|
192
315
|
timer: c
|
|
193
|
-
}), this.
|
|
316
|
+
}), this.sendToSeat(e, {
|
|
194
317
|
k: "call",
|
|
195
|
-
id:
|
|
318
|
+
id: o,
|
|
196
319
|
gen: t,
|
|
197
320
|
method: n,
|
|
198
321
|
args: r
|
|
@@ -250,70 +373,100 @@ var s = new Set(i), c = class i {
|
|
|
250
373
|
type: "players",
|
|
251
374
|
players: this.players
|
|
252
375
|
});
|
|
253
|
-
let n = this.session, r =
|
|
376
|
+
let n = this.session, r = this.seatOf.get(e.id) ?? null;
|
|
254
377
|
switch (t) {
|
|
255
378
|
case "join":
|
|
256
|
-
if (n && !n.ended && !
|
|
379
|
+
if (n && !n.ended && r === null && !this.seatsMode) try {
|
|
257
380
|
n.addPlayer({
|
|
258
381
|
id: e.id,
|
|
259
382
|
name: e.name
|
|
260
|
-
}), this.connectRemote(e.id);
|
|
383
|
+
}), this.seatOf.set(e.id, e.id), this.connectRemote(e.id);
|
|
261
384
|
} catch {}
|
|
262
385
|
break;
|
|
263
386
|
case "disconnect": {
|
|
264
|
-
let
|
|
265
|
-
|
|
387
|
+
let e = r === null ? void 0 : this.channels.get(r);
|
|
388
|
+
e && this.settleCalls(e), n && r !== null && n.setConnected(r, !1);
|
|
266
389
|
break;
|
|
267
390
|
}
|
|
268
391
|
case "rename":
|
|
269
|
-
r && n.renamePlayer(
|
|
392
|
+
n && r !== null && !this.seatsMode && n.renamePlayer(r, e.name);
|
|
270
393
|
break;
|
|
271
|
-
case "leave": if (this.
|
|
272
|
-
|
|
273
|
-
|
|
394
|
+
case "leave": if (this.appVersions.delete(e.id), r !== null && this.seatsMode) this.unseat(e.id), this.seatsChanged();
|
|
395
|
+
else if (r !== null) {
|
|
396
|
+
this.seatOf.delete(e.id), this.channels.delete(r);
|
|
397
|
+
try {
|
|
398
|
+
n?.removePlayer(r);
|
|
399
|
+
} catch {}
|
|
400
|
+
}
|
|
274
401
|
}
|
|
275
402
|
}
|
|
276
403
|
async onPlayer(e, t) {
|
|
277
404
|
if (typeof t != "object" || !t) return;
|
|
278
|
-
let
|
|
279
|
-
|
|
280
|
-
|
|
405
|
+
let a = this.seatOf.get(e) ?? null;
|
|
406
|
+
if (t.k === "hello") this.appVersions.set(e, typeof t.v == "number" && Number.isFinite(t.v) ? t.v : 0), this.sendTo(e, {
|
|
407
|
+
k: "state",
|
|
408
|
+
state: this.state
|
|
409
|
+
}), this.seatsMode && this.sendSeat(e);
|
|
410
|
+
else if (t.k === "claim") {
|
|
411
|
+
this.claim(e, t.seat);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (a === null) {
|
|
415
|
+
if (t.k === "call") {
|
|
416
|
+
let n = u.has(t.method) ? { message: "[SESSION_ENDED] This device has no place in the game." } : i(t.method);
|
|
281
417
|
this.sendTo(e, {
|
|
282
|
-
k: "
|
|
283
|
-
|
|
418
|
+
k: "result",
|
|
419
|
+
id: t.id,
|
|
420
|
+
ok: !1,
|
|
421
|
+
error: n
|
|
284
422
|
});
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
423
|
+
}
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
let o = this.channel(a);
|
|
427
|
+
switch (t.k) {
|
|
428
|
+
case "hello": {
|
|
429
|
+
let e = this.session;
|
|
430
|
+
if (!e || !e.players.some((e) => e.id === a)) return;
|
|
431
|
+
if (o.pendingMount) {
|
|
432
|
+
this.sendMount(a, o.pendingMount.gen);
|
|
289
433
|
return;
|
|
290
434
|
}
|
|
291
|
-
|
|
435
|
+
e.setConnected(a, !0), (o.gen === 0 || t.gen !== o.gen) && (e.getInstance(a) ? e.reload(a).catch(() => {}) : this.connectRemote(a));
|
|
292
436
|
return;
|
|
293
437
|
}
|
|
294
438
|
case "mounted":
|
|
295
|
-
if (
|
|
296
|
-
clearTimeout(
|
|
297
|
-
let e =
|
|
298
|
-
|
|
439
|
+
if (o.pendingMount?.gen === t.gen) {
|
|
440
|
+
clearTimeout(o.pendingMount.timer);
|
|
441
|
+
let e = o.pendingMount;
|
|
442
|
+
o.pendingMount = null, e.resolve();
|
|
299
443
|
}
|
|
300
444
|
return;
|
|
301
445
|
case "mount-failed":
|
|
302
|
-
if (
|
|
303
|
-
clearTimeout(
|
|
304
|
-
let e =
|
|
305
|
-
|
|
446
|
+
if (o.pendingMount?.gen === t.gen) {
|
|
447
|
+
clearTimeout(o.pendingMount.timer);
|
|
448
|
+
let e = o.pendingMount;
|
|
449
|
+
o.pendingMount = null, e.reject(Error(t.error?.message ?? "The game could not be loaded on the device."));
|
|
306
450
|
}
|
|
307
451
|
return;
|
|
308
452
|
case "result": {
|
|
309
|
-
let e =
|
|
453
|
+
let e = o.calls.get(t.id);
|
|
310
454
|
if (!e) return;
|
|
311
|
-
|
|
455
|
+
o.calls.delete(t.id), clearTimeout(e.timer), t.ok ? e.resolve(t.value) : e.reject(Error(t.error?.message ?? "Remote call failed"));
|
|
312
456
|
return;
|
|
313
457
|
}
|
|
314
458
|
case "call": {
|
|
315
459
|
let a = (t) => this.sendTo(e, t);
|
|
316
|
-
if (
|
|
460
|
+
if (!u.has(t.method)) {
|
|
461
|
+
a({
|
|
462
|
+
k: "result",
|
|
463
|
+
id: t.id,
|
|
464
|
+
ok: !1,
|
|
465
|
+
error: i(t.method)
|
|
466
|
+
});
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (t.gen !== o.gen || !o.hostApi || !Array.isArray(t.args)) {
|
|
317
470
|
a({
|
|
318
471
|
k: "result",
|
|
319
472
|
id: t.id,
|
|
@@ -323,26 +476,41 @@ var s = new Set(i), c = class i {
|
|
|
323
476
|
return;
|
|
324
477
|
}
|
|
325
478
|
try {
|
|
326
|
-
let
|
|
479
|
+
let r = o.hostApi[t.method], i = await r(...t.args);
|
|
327
480
|
if (t.method === "hello") {
|
|
328
|
-
|
|
329
|
-
...
|
|
481
|
+
i = {
|
|
482
|
+
...i,
|
|
330
483
|
set: null
|
|
331
484
|
}, a({
|
|
332
485
|
k: "result",
|
|
333
486
|
id: t.id,
|
|
334
487
|
ok: !0,
|
|
335
|
-
value:
|
|
488
|
+
value: i,
|
|
336
489
|
hostNow: Date.now()
|
|
337
490
|
});
|
|
338
491
|
return;
|
|
339
492
|
}
|
|
340
|
-
t.method === "getAsset" &&
|
|
493
|
+
t.method === "getAsset" && i instanceof Blob && (i = await n(i));
|
|
494
|
+
let s = {
|
|
341
495
|
k: "result",
|
|
342
496
|
id: t.id,
|
|
343
497
|
ok: !0,
|
|
344
|
-
value:
|
|
345
|
-
}
|
|
498
|
+
value: i ?? null
|
|
499
|
+
};
|
|
500
|
+
if (l({
|
|
501
|
+
t: "to",
|
|
502
|
+
to: e,
|
|
503
|
+
data: s
|
|
504
|
+
}) > c.frameBytes) {
|
|
505
|
+
a({
|
|
506
|
+
k: "result",
|
|
507
|
+
id: t.id,
|
|
508
|
+
ok: !1,
|
|
509
|
+
error: { message: `[DATA_TOO_LARGE] The result of ${t.method} is too large to send to the device.` }
|
|
510
|
+
});
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
a(s);
|
|
346
514
|
} catch (e) {
|
|
347
515
|
a({
|
|
348
516
|
k: "result",
|
|
@@ -362,9 +530,13 @@ var s = new Set(i), c = class i {
|
|
|
362
530
|
data: t
|
|
363
531
|
});
|
|
364
532
|
}
|
|
533
|
+
sendToSeat(e, t) {
|
|
534
|
+
let n = this.relayIdOf(e);
|
|
535
|
+
n !== null && this.sendTo(n, t);
|
|
536
|
+
}
|
|
365
537
|
broadcast(e) {
|
|
366
538
|
for (let t of this.players) t.connected && this.sendTo(t.id, e);
|
|
367
539
|
}
|
|
368
540
|
};
|
|
369
541
|
//#endregion
|
|
370
|
-
export {
|
|
542
|
+
export { d as RelayHost };
|
package/dist/relay/messages.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function
|
|
1
|
+
var e=1;function t(e){let t=e instanceof Error?e.message:String(e);return e?.code===`METHOD_NOT_FOUND`?{message:`[UNKNOWN_METHOD] ${t}`}:{message:t}}function n(e){return{message:`[UNKNOWN_METHOD] This app does not know the method "${String(e).slice(0,64)}".`}}async function r(e){let t=new Uint8Array(await e.arrayBuffer()),n=``;for(let e=0;e<t.length;e+=32768)n+=String.fromCharCode(...t.subarray(e,e+32768));return{$blob:btoa(n),type:e.type}}function i(e){let t=atob(e.$blob),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return new Blob([n],{type:e.type})}function a(e){return typeof e==`object`&&!!e&&typeof e.$blob==`string`}exports.RELAY_APP_VERSION=e,exports.blobToJson=r,exports.isSerializedBlob=a,exports.jsonToBlob=i,exports.serializeError=t,exports.unknownMethod=n;
|
package/dist/relay/messages.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
//#region src/relay/messages.ts
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
var e = 1;
|
|
3
|
+
function t(e) {
|
|
4
|
+
let t = e instanceof Error ? e.message : String(e);
|
|
5
|
+
return e?.code === "METHOD_NOT_FOUND" ? { message: `[UNKNOWN_METHOD] ${t}` } : { message: t };
|
|
4
6
|
}
|
|
5
|
-
|
|
7
|
+
function n(e) {
|
|
8
|
+
return { message: `[UNKNOWN_METHOD] This app does not know the method "${String(e).slice(0, 64)}".` };
|
|
9
|
+
}
|
|
10
|
+
async function r(e) {
|
|
6
11
|
let t = new Uint8Array(await e.arrayBuffer()), n = "";
|
|
7
12
|
for (let e = 0; e < t.length; e += 32768) n += String.fromCharCode(...t.subarray(e, e + 32768));
|
|
8
13
|
return {
|
|
@@ -10,13 +15,13 @@ async function t(e) {
|
|
|
10
15
|
type: e.type
|
|
11
16
|
};
|
|
12
17
|
}
|
|
13
|
-
function
|
|
18
|
+
function i(e) {
|
|
14
19
|
let t = atob(e.$blob), n = new Uint8Array(t.length);
|
|
15
20
|
for (let e = 0; e < t.length; e++) n[e] = t.charCodeAt(e);
|
|
16
21
|
return new Blob([n], { type: e.type });
|
|
17
22
|
}
|
|
18
|
-
function
|
|
23
|
+
function a(e) {
|
|
19
24
|
return typeof e == "object" && !!e && typeof e.$blob == "string";
|
|
20
25
|
}
|
|
21
26
|
//#endregion
|
|
22
|
-
export {
|
|
27
|
+
export { e as RELAY_APP_VERSION, r as blobToJson, a as isSerializedBlob, i as jsonToBlob, t as serializeError, n as unknownMethod };
|
package/dist/relay/player.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=require("../frame.cjs"),t=require("./socket.cjs"),n=require("./messages.cjs");let r=require("@memizy/protocol");var i=new Set(r.PLUGIN_API_METHODS),a=class{playerId=null;token;name;state=null;hostConnected=!0;options;socket;listeners=new Set;bundle=null;frame=null;plugin=null;gen=0;callCounter=0;pending=new Map;skewMs=0;hostConfig=null;mountQueue=Promise.resolve();constructor(e){this.options=e,this.token=e.token??null,this.name=e.name,this.socket=new t.RelaySocket({url:t.relayWebSocketUrl(e.serverUrl),attach:()=>({t:`join`,protocol:r.RELAY_PROTOCOL,pin:e.pin,name:this.name,token:this.token??void 0}),WebSocket:e.WebSocket}),this.socket.onStatus(e=>this.emit({type:`status`,status:e})),this.socket.onMessage(e=>this.onServer(e))}get status(){return this.socket.status}on(e){return this.listeners.add(e),()=>this.listeners.delete(e)}emit(e){for(let t of this.listeners)t(e)}
|
|
1
|
+
const e=require("../frame.cjs"),t=require("./socket.cjs"),n=require("./messages.cjs");let r=require("@memizy/protocol");var i=new Set(r.PLUGIN_API_METHODS),a=1e3,o=class{playerId=null;token;name;state=null;hostConnected=!0;hostAppVersion=null;seat=void 0;options;socket;listeners=new Set;bundle=null;frame=null;plugin=null;gen=0;mountGen=0;callCounter=0;pending=new Map;skewMs=0;hostConfig=null;mountQueue=Promise.resolve();sessionEndedSent=!1;constructor(e){this.options=e,this.token=e.token??null,this.name=e.name,this.socket=new t.RelaySocket({url:t.relayWebSocketUrl(e.serverUrl),attach:()=>({t:`join`,protocol:r.RELAY_PROTOCOL,pin:e.pin,name:this.name,token:this.token??void 0,...e.auth!==void 0&&{auth:e.auth}}),WebSocket:e.WebSocket}),this.socket.onStatus(e=>this.emit({type:`status`,status:e})),this.socket.onMessage(e=>this.onServer(e))}get status(){return this.socket.status}on(e){return this.listeners.add(e),()=>this.listeners.delete(e)}emit(e){for(let t of this.listeners)t(e)}refreshConfig(){this.plugin&&this.hostConfig&&this.plugin.configChanged(this.localConfig(this.hostConfig)).catch(()=>{})}localConfig(e){return{...e,...this.options.config?.()}}rename(e){this.name=e,this.socket.send({t:`rename`,name:e})}claimSeat(e){this.up({k:`claim`,seat:e})}reconnectNow(){this.socket.reconnectNow()}leave(){this.destroyFrame(),this.socket.close()}onServer(e){switch(e.t){case`joined`:this.playerId=e.playerId,this.token=e.token,this.name=e.name,this.hostConnected=e.hostConnected,this.emit({type:`joined`,playerId:e.playerId,token:e.token,name:e.name}),this.up({k:`hello`,gen:this.plugin?this.gen:0,v:1});break;case`host-presence`:this.hostConnected=e.connected,this.emit({type:`host`,connected:e.connected}),e.connected&&this.up({k:`hello`,gen:this.plugin?this.gen:0,v:1});break;case`data`:this.onHost(e.data);break;case`error`:this.emit({type:`error`,code:e.code,message:e.message,fatal:e.fatal});break;case`closed`:{let t=e.reason===`kicked`?`kicked`:e.reason===`host`?`closed`:`host-left`;this.mountQueue=this.mountQueue.then(async()=>{await this.endGame(t),this.emit({type:`closed`,reason:e.reason})});break}}}up(e){return this.socket.send({t:`up`,data:e})}async onHost(e){if(typeof e==`object`&&e)switch(e.k){case`state`:e.state.seats||(this.seat=void 0),this.state=e.state,this.emit({type:`state`,state:e.state});return;case`mount`:if(this.hostAppVersion=typeof e.v==`number`?e.v:0,e.gen===this.mountGen){this.mountQueue=this.mountQueue.then(()=>{this.plugin&&this.gen===e.gen&&this.up({k:`mounted`,gen:e.gen})});return}this.mountGen=e.gen,this.mountQueue=this.mountQueue.then(()=>this.mount(e.gen,e.bundle));return;case`unmount`:this.mountGen=0,this.mountQueue=this.mountQueue.then(()=>this.endGame(`closed`));return;case`seat`:if(e.seat!==null&&typeof e.seat!=`string`)return;this.seat=e.seat,e.seat!==null&&typeof e.name==`string`&&e.name&&e.name!==this.name&&this.rename(e.name),this.emit({type:`seat`,seat:e.seat});return;case`result`:{let t=this.pending.get(e.id);if(!t)return;if(this.pending.delete(e.id),clearTimeout(t.timer),!e.ok){t.reject(Error(e.error?.message??`Host call failed`));return}let r=e.value;if(t.method===`hello`&&this.bundle){if(e.hostNow!==void 0){let n=Date.now();this.skewMs=e.hostNow+(n-t.sentAt)/2-n}let n=r;this.hostConfig=n.config,r={...n,set:structuredClone(this.bundle.data.set),clock:{...n.clock,offsetMs:n.clock.offsetMs+this.skewMs},config:this.localConfig(n.config)}}else n.isSerializedBlob(r)&&(r=n.jsonToBlob(r));t.resolve(r);return}case`call`:{let t=e=>this.up(e);if(!i.has(e.method)){t({k:`result`,id:e.id,ok:!1,error:n.unknownMethod(e.method)});return}if(e.gen!==this.gen||!this.plugin){t({k:`result`,id:e.id,ok:!1,error:{message:`[SESSION_ENDED] No active plugin instance.`}});return}let r=e.args;if(e.method===`sessionEnded`&&(this.sessionEndedSent=!0),e.method===`configChanged`&&(this.hostConfig=r[0],r=[this.localConfig(this.hostConfig)]),e.method===`clockChanged`){let e=r[0];r=[{...e,offsetMs:e.offsetMs+this.skewMs}]}try{let n=this.plugin[e.method],i=await n(...r);t({k:`result`,id:e.id,ok:!0,value:i??null})}catch(r){t({k:`result`,id:e.id,ok:!1,error:n.serializeError(r)})}return}}}async ensureBundle(e){if(this.bundle?.version===e)return this.bundle.data;let t=await(this.options.fetch??fetch)(new URL(`/api/rooms/${this.options.pin}/bundle`,this.options.serverUrl));if(!t.ok)throw Error(`Could not download the game (${t.status}).`);let n=await t.json();return this.bundle={version:Number(t.headers.get(`X-Bundle-Version`)??e),data:n},n}async endGame(e){let t=this.plugin;if(t&&!this.sessionEndedSent){this.sessionEndedSent=!0;let n;await Promise.race([t.sessionEnded(e).catch(()=>{}),new Promise(e=>n=setTimeout(e,a))]),clearTimeout(n)}this.destroyFrame()}async mount(t,i){this.destroyFrame(),this.sessionEndedSent=!1,this.gen=t;try{let n=await this.ensureBundle(i);if(this.gen!==t)return;let a=this.options.container();if(!a)throw Error(`No place for the game on this page.`);let o=(0,r.extractManifestFromHtml)(n.pluginHtml),s=o.success?String(o.data.appName??`Game`):`Game`,c=this.options.transformHtml?this.options.transformHtml(n.pluginHtml):n.pluginHtml,l=(this.options.createFrame??e.createPluginFrame)(c,s,this.hostApiFor(t),a.ownerDocument);this.frame=l,a.replaceChildren(l.iframe),this.emit({type:`frame`,active:!0});let u=await l.plugin;if(this.gen!==t)return;this.plugin=u,this.up({k:`mounted`,gen:t})}catch(e){if(this.gen!==t)return;this.emit({type:`error`,code:`MOUNT_FAILED`,message:e.message,fatal:!1}),this.up({k:`mount-failed`,gen:t,error:n.serializeError(e)})}}hostApiFor(e){let t={};for(let n of r.HOST_API_METHODS)t[n]=(...t)=>{if(this.gen!==e)return Promise.reject(Error(`[SESSION_ENDED] This plugin instance was replaced.`));let r=++this.callCounter;return new Promise((i,a)=>{let o=setTimeout(()=>{this.pending.delete(r),a(Error(`[INTERNAL_ERROR] The host did not answer ${n} in time.`))},this.options.callTimeoutMs??15e3);this.pending.set(r,{method:n,sentAt:Date.now(),resolve:i,reject:a,timer:o}),this.up({k:`call`,id:r,gen:e,method:n,args:t})||(clearTimeout(o),this.pending.delete(r),n===`hello`?a(Error(`[INTERNAL_ERROR] Offline.`)):i(void 0))})};return t}destroyFrame(){let e=this.frame!==null;this.frame?.destroy(),this.frame?.iframe.remove(),this.frame=null,this.plugin=null,this.gen=0;for(let e of this.pending.values())clearTimeout(e.timer),e.reject(Error(`[SESSION_ENDED] The plugin instance was closed.`));this.pending.clear(),e&&this.emit({type:`frame`,active:!1})}};exports.RelayPlayer=o;
|