@bountyboard/arcade-sdk 1.0.1 → 1.2.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/AGENTS.md +10 -5
- package/CHANGELOG.md +68 -0
- package/README.md +331 -75
- package/dist/{chunk-OWOESH4X.js → chunk-KDBBR532.js} +51 -9
- package/dist/chunk-KDBBR532.js.map +1 -0
- package/dist/global-sdk.d.ts +66 -3
- package/dist/global.js +175 -26
- package/dist/index.cjs +50 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/multiplayer.cjs +175 -26
- package/dist/multiplayer.cjs.map +1 -1
- package/dist/multiplayer.d.cts +2 -2
- package/dist/multiplayer.d.ts +2 -2
- package/dist/multiplayer.js +126 -19
- package/dist/multiplayer.js.map +1 -1
- package/dist/{types-BKChtV3H.d.cts → types-dtsNXYZW.d.cts} +69 -3
- package/dist/{types-BKChtV3H.d.ts → types-dtsNXYZW.d.ts} +69 -3
- package/docs/external-authoritative-servers.md +265 -0
- package/docs/llms.txt +715 -0
- package/docs/relay-rooms.md +134 -0
- package/package.json +6 -3
- package/dist/chunk-OWOESH4X.js.map +0 -1
package/dist/multiplayer.cjs
CHANGED
|
@@ -37,6 +37,8 @@ var AD_PLACEMENT_SRC = "https://pagead2.googlesyndication.com/pagead/js/adsbygoo
|
|
|
37
37
|
var AD_PLACEMENT_SCRIPT_ID = "bb-arcade-h5-ads";
|
|
38
38
|
var DEFAULT_AD_LOAD_TIMEOUT_MS = 6e3;
|
|
39
39
|
var SAVE_TIMEOUT_MS = 15e3;
|
|
40
|
+
var LOCK_TIMEOUT_MS = 5e3;
|
|
41
|
+
var MAX_SAVE_BYTES = 1024 * 1024;
|
|
40
42
|
var INIT_CONFIG_GRACE_MS = 1500;
|
|
41
43
|
var DEFAULT_ALLOW = [
|
|
42
44
|
"bountyboard.gg",
|
|
@@ -61,6 +63,14 @@ function cleanText(value, fallback, max) {
|
|
|
61
63
|
if (typeof value !== "string") return fallback || "";
|
|
62
64
|
return value.replace(/[^\w:.-]+/g, "_").slice(0, max || 80) || fallback || "";
|
|
63
65
|
}
|
|
66
|
+
var cachedTextEncoder = null;
|
|
67
|
+
function utf8ByteLength(value) {
|
|
68
|
+
if (typeof TextEncoder !== "undefined") {
|
|
69
|
+
cachedTextEncoder = cachedTextEncoder || new TextEncoder();
|
|
70
|
+
return cachedTextEncoder.encode(value).length;
|
|
71
|
+
}
|
|
72
|
+
return new Blob([value]).size;
|
|
73
|
+
}
|
|
64
74
|
function toBoolean(value) {
|
|
65
75
|
return value === true || value === "true" || value === "1";
|
|
66
76
|
}
|
|
@@ -180,13 +190,14 @@ function createBBArcade() {
|
|
|
180
190
|
};
|
|
181
191
|
let hostConfigReceived = false;
|
|
182
192
|
let hostPlayer = null;
|
|
193
|
+
const playerChangeHandlers = [];
|
|
183
194
|
let blocked = false;
|
|
184
195
|
const pending = {};
|
|
185
196
|
let seq = 0;
|
|
186
197
|
function post(type, score, extra) {
|
|
187
198
|
if (blocked) return;
|
|
188
199
|
const message = { source: SDK_MESSAGE_SOURCE, v: VERSION, type };
|
|
189
|
-
if (typeof score === "number" && isFinite(score)) message.score = Math.
|
|
200
|
+
if (typeof score === "number" && isFinite(score)) message.score = Math.trunc(score);
|
|
190
201
|
if (extra && typeof extra === "object") {
|
|
191
202
|
const fields = extra;
|
|
192
203
|
for (const key in fields) {
|
|
@@ -207,7 +218,7 @@ function createBBArcade() {
|
|
|
207
218
|
return true;
|
|
208
219
|
}
|
|
209
220
|
}
|
|
210
|
-
function request(type, payload) {
|
|
221
|
+
function request(type, payload, timeoutMs = SAVE_TIMEOUT_MS) {
|
|
211
222
|
return new Promise(function(resolve, reject) {
|
|
212
223
|
if (blocked) return reject(sdkError("error"));
|
|
213
224
|
if (!isEmbedded()) return reject(sdkError("unsupported"));
|
|
@@ -217,7 +228,7 @@ function createBBArcade() {
|
|
|
217
228
|
delete pending[requestId];
|
|
218
229
|
reject(sdkError("error"));
|
|
219
230
|
}
|
|
220
|
-
},
|
|
231
|
+
}, timeoutMs);
|
|
221
232
|
pending[requestId] = { resolve, reject, timer };
|
|
222
233
|
try {
|
|
223
234
|
window.parent.postMessage(
|
|
@@ -309,7 +320,7 @@ function createBBArcade() {
|
|
|
309
320
|
if (!hostnameAllowed(origin, allow)) blockHost(options);
|
|
310
321
|
return;
|
|
311
322
|
}
|
|
312
|
-
request("host").then(
|
|
323
|
+
request("host", void 0, LOCK_TIMEOUT_MS).then(
|
|
313
324
|
function(res) {
|
|
314
325
|
const r = res;
|
|
315
326
|
if (!hostnameAllowed(r && r.origin, allow)) blockHost(options);
|
|
@@ -333,7 +344,7 @@ function createBBArcade() {
|
|
|
333
344
|
lockToHostBestEffort(opts, allow);
|
|
334
345
|
return;
|
|
335
346
|
}
|
|
336
|
-
request("host_attest").then(
|
|
347
|
+
request("host_attest", void 0, LOCK_TIMEOUT_MS).then(
|
|
337
348
|
function(res) {
|
|
338
349
|
const r = res;
|
|
339
350
|
if (!r || !r.attestation) {
|
|
@@ -395,7 +406,15 @@ function createBBArcade() {
|
|
|
395
406
|
if (msg.source !== HOST_MESSAGE_SOURCE || msg.type !== "config") return;
|
|
396
407
|
hostConfigReceived = true;
|
|
397
408
|
if (msg.player !== void 0) {
|
|
398
|
-
|
|
409
|
+
const nextPlayer = sanitizePlayer(msg.player);
|
|
410
|
+
const changed = nextPlayer === null ? hostPlayer !== null : hostPlayer === null || nextPlayer.name !== hostPlayer.name || nextPlayer.avatarUrl !== hostPlayer.avatarUrl;
|
|
411
|
+
hostPlayer = nextPlayer;
|
|
412
|
+
if (changed) {
|
|
413
|
+
const snapshot = nextPlayer ? { name: nextPlayer.name, avatarUrl: nextPlayer.avatarUrl } : null;
|
|
414
|
+
for (let i = 0; i < playerChangeHandlers.length; i++) {
|
|
415
|
+
safeCall(playerChangeHandlers[i], snapshot);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
399
418
|
}
|
|
400
419
|
const rewardedAds = msg.rewardedAds;
|
|
401
420
|
if (rewardedAds && typeof rewardedAds === "object") {
|
|
@@ -527,6 +546,7 @@ function createBBArcade() {
|
|
|
527
546
|
let resolved = false;
|
|
528
547
|
let status = "unavailable";
|
|
529
548
|
let rewardOffered = false;
|
|
549
|
+
let adShown = false;
|
|
530
550
|
const waitTimer = setTimeout(function() {
|
|
531
551
|
if (rewardOffered || resolved) return;
|
|
532
552
|
const timeout = Object.assign(
|
|
@@ -571,6 +591,7 @@ function createBBArcade() {
|
|
|
571
591
|
}
|
|
572
592
|
post("rewarded_ad", void 0, Object.assign({ event: "started" }, base));
|
|
573
593
|
try {
|
|
594
|
+
adShown = true;
|
|
574
595
|
showAdFn();
|
|
575
596
|
} catch (err) {
|
|
576
597
|
const showError = Object.assign(
|
|
@@ -620,7 +641,13 @@ function createBBArcade() {
|
|
|
620
641
|
},
|
|
621
642
|
adBreakDone: function(placementInfo) {
|
|
622
643
|
if (resolved) return;
|
|
623
|
-
const result = rewardOffered || status !== "unavailable" ? {
|
|
644
|
+
const result = rewardOffered || status !== "unavailable" ? {
|
|
645
|
+
// A reward was offered but the ad never reached the screen
|
|
646
|
+
// (the game retained show() and let the break lapse): the
|
|
647
|
+
// break is over without a view, so resolve 'dismissed',
|
|
648
|
+
// not the non-terminal-looking 'ready'.
|
|
649
|
+
status: status === "ready" && !adShown ? "dismissed" : status
|
|
650
|
+
} : { status: "unavailable", error: "no_rewarded_ad" };
|
|
624
651
|
if (placementInfo && typeof placementInfo === "object") {
|
|
625
652
|
result.breakStatus = cleanText(
|
|
626
653
|
placementInfo.breakStatus
|
|
@@ -823,12 +850,25 @@ function createBBArcade() {
|
|
|
823
850
|
prepareRewardedBreak: prepareRewardedAd,
|
|
824
851
|
rewardedBreak,
|
|
825
852
|
save: function(blob) {
|
|
826
|
-
|
|
853
|
+
const data = String(blob);
|
|
854
|
+
if (utf8ByteLength(data) > MAX_SAVE_BYTES) {
|
|
855
|
+
return Promise.reject(sdkError("too_large"));
|
|
856
|
+
}
|
|
857
|
+
return request("save", data);
|
|
827
858
|
},
|
|
828
859
|
load: function() {
|
|
829
860
|
return request("load");
|
|
830
861
|
},
|
|
831
862
|
getPlayer,
|
|
863
|
+
onPlayerChange: function(handler) {
|
|
864
|
+
if (typeof handler !== "function") return function() {
|
|
865
|
+
};
|
|
866
|
+
playerChangeHandlers.push(handler);
|
|
867
|
+
return function() {
|
|
868
|
+
const idx = playerChangeHandlers.indexOf(handler);
|
|
869
|
+
if (idx !== -1) playerChangeHandlers.splice(idx, 1);
|
|
870
|
+
};
|
|
871
|
+
},
|
|
832
872
|
getVariant: function(key, variants) {
|
|
833
873
|
const sorted = Array.isArray(variants) ? variants.slice().sort() : [];
|
|
834
874
|
const fallback = sorted.length > 0 ? sorted[0] : null;
|
|
@@ -895,6 +935,8 @@ function createInertBBArcade() {
|
|
|
895
935
|
save: () => Promise.reject(sdkError2("unsupported")),
|
|
896
936
|
load: () => Promise.reject(sdkError2("unsupported")),
|
|
897
937
|
getPlayer: () => Promise.resolve(null),
|
|
938
|
+
onPlayerChange: () => () => {
|
|
939
|
+
},
|
|
898
940
|
getVariant: (key, variants) => {
|
|
899
941
|
const sorted = Array.isArray(variants) ? variants.slice().sort() : [];
|
|
900
942
|
return Promise.resolve(sorted.length > 0 ? sorted[0] : null);
|
|
@@ -908,12 +950,17 @@ var BBArcade = typeof window === "undefined" ? createInertBBArcade() : createBBA
|
|
|
908
950
|
|
|
909
951
|
// src/multiplayer.ts
|
|
910
952
|
var WELCOME_TIMEOUT_MS = 1e4;
|
|
953
|
+
var MIN_WELCOME_TIMEOUT_MS = 1e3;
|
|
954
|
+
var MAX_WELCOME_TIMEOUT_MS = 6e4;
|
|
911
955
|
var MAX_RECONNECT_ATTEMPTS = 3;
|
|
956
|
+
var MAX_JOIN_DATA_BYTES = 1024;
|
|
957
|
+
var SEAT_FULL_CLOSE_CODE = 4005;
|
|
912
958
|
var CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
913
959
|
var CODE_LENGTH = 4;
|
|
914
|
-
function sdkError3(code) {
|
|
960
|
+
function sdkError3(code, detail) {
|
|
915
961
|
const err = new Error(code);
|
|
916
962
|
err.code = code;
|
|
963
|
+
if (detail) err.detail = detail;
|
|
917
964
|
return err;
|
|
918
965
|
}
|
|
919
966
|
function randomChars(length) {
|
|
@@ -930,27 +977,51 @@ function randomChars(length) {
|
|
|
930
977
|
function generateCode() {
|
|
931
978
|
return randomChars(CODE_LENGTH);
|
|
932
979
|
}
|
|
933
|
-
var
|
|
934
|
-
function
|
|
935
|
-
if (
|
|
936
|
-
|
|
980
|
+
var cachedTextEncoder2 = null;
|
|
981
|
+
function utf8ByteLength2(value) {
|
|
982
|
+
if (typeof TextEncoder !== "undefined") {
|
|
983
|
+
cachedTextEncoder2 = cachedTextEncoder2 || new TextEncoder();
|
|
984
|
+
return cachedTextEncoder2.encode(value).length;
|
|
985
|
+
}
|
|
986
|
+
return new Blob([value]).size;
|
|
937
987
|
}
|
|
938
|
-
function
|
|
988
|
+
function serializeJoinData(data) {
|
|
989
|
+
if (data === void 0) return null;
|
|
990
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) throw sdkError3("rejected");
|
|
991
|
+
let serialized;
|
|
992
|
+
try {
|
|
993
|
+
serialized = JSON.stringify(data);
|
|
994
|
+
} catch (e) {
|
|
995
|
+
throw sdkError3("rejected");
|
|
996
|
+
}
|
|
997
|
+
if (!serialized || utf8ByteLength2(serialized) > MAX_JOIN_DATA_BYTES) {
|
|
998
|
+
throw sdkError3("rejected");
|
|
999
|
+
}
|
|
1000
|
+
return serialized;
|
|
1001
|
+
}
|
|
1002
|
+
function requestTicket(payload) {
|
|
939
1003
|
const request = BBArcade._request;
|
|
940
1004
|
if (typeof request !== "function") return Promise.reject(sdkError3("unsupported"));
|
|
941
|
-
return request("mp_ticket",
|
|
1005
|
+
return request("mp_ticket", payload).then((grant) => {
|
|
942
1006
|
const g = grant;
|
|
943
1007
|
if (!g || typeof g.ticket !== "string" || typeof g.roomUrl !== "string") {
|
|
944
1008
|
throw sdkError3("error");
|
|
945
1009
|
}
|
|
946
|
-
return {
|
|
1010
|
+
return {
|
|
1011
|
+
ticket: g.ticket,
|
|
1012
|
+
roomUrl: g.roomUrl,
|
|
1013
|
+
expiresAt: Number(g.expiresAt) || 0,
|
|
1014
|
+
roomId: typeof g.roomId === "string" && g.roomId ? g.roomId : null
|
|
1015
|
+
};
|
|
947
1016
|
});
|
|
948
1017
|
}
|
|
949
1018
|
var RoomConnection = class {
|
|
950
|
-
constructor(code, devGrant) {
|
|
1019
|
+
constructor(code, devGrant, joinData, welcomeTimeoutMs = WELCOME_TIMEOUT_MS) {
|
|
951
1020
|
this.playerId = "";
|
|
952
1021
|
this.players = [];
|
|
953
1022
|
this.state = null;
|
|
1023
|
+
this.connected = false;
|
|
1024
|
+
this.latencyMs = null;
|
|
954
1025
|
this.ws = null;
|
|
955
1026
|
// Keyed by event name; values are erased to (unknown) => void internally and
|
|
956
1027
|
// retyped at the on()/emit() boundary, which keeps the public API strictly
|
|
@@ -963,14 +1034,20 @@ var RoomConnection = class {
|
|
|
963
1034
|
// or the welcome timed out — permanent outcomes that must not reconnect.
|
|
964
1035
|
this.terminal = false;
|
|
965
1036
|
this.reconnectAttempts = 0;
|
|
1037
|
+
// The in-flight connect()'s settlement, so leave() can reject a join that
|
|
1038
|
+
// is still waiting on its welcome instead of stranding it on the timeout.
|
|
1039
|
+
this.pendingConnect = null;
|
|
966
1040
|
this.code = code;
|
|
967
1041
|
this.devGrant = devGrant;
|
|
1042
|
+
this.joinData = joinData;
|
|
1043
|
+
this.welcomeTimeoutMs = welcomeTimeoutMs;
|
|
968
1044
|
}
|
|
969
1045
|
/** Connect and resolve on the server's welcome (post-ticket-verification). */
|
|
970
1046
|
connect(grant) {
|
|
971
1047
|
return new Promise((resolve, reject) => {
|
|
972
1048
|
let settled = false;
|
|
973
|
-
const url = grant.roomUrl + (grant.roomUrl.indexOf("?") === -1 ? "?" : "&") + "ticket=" + encodeURIComponent(grant.ticket);
|
|
1049
|
+
const url = grant.roomUrl + (grant.roomUrl.indexOf("?") === -1 ? "?" : "&") + "ticket=" + encodeURIComponent(grant.ticket) + (this.joinData === null ? "" : "&join=" + encodeURIComponent(this.joinData));
|
|
1050
|
+
const connectStartedAt = Date.now();
|
|
974
1051
|
let ws;
|
|
975
1052
|
try {
|
|
976
1053
|
ws = new WebSocket(url);
|
|
@@ -981,13 +1058,15 @@ var RoomConnection = class {
|
|
|
981
1058
|
const welcomeTimer = setTimeout(() => {
|
|
982
1059
|
if (settled) return;
|
|
983
1060
|
settled = true;
|
|
1061
|
+
this.pendingConnect = null;
|
|
984
1062
|
this.terminal = true;
|
|
985
1063
|
try {
|
|
986
1064
|
ws.close();
|
|
987
1065
|
} catch (err) {
|
|
988
1066
|
}
|
|
989
1067
|
reject(sdkError3("error"));
|
|
990
|
-
},
|
|
1068
|
+
}, this.welcomeTimeoutMs);
|
|
1069
|
+
this.pendingConnect = { reject, timer: welcomeTimer };
|
|
991
1070
|
ws.onmessage = (event) => {
|
|
992
1071
|
var _a, _b, _c, _d, _e;
|
|
993
1072
|
let msg;
|
|
@@ -998,15 +1077,27 @@ var RoomConnection = class {
|
|
|
998
1077
|
}
|
|
999
1078
|
if (!msg || typeof msg.t !== "string") return;
|
|
1000
1079
|
if (msg.t === "welcome") {
|
|
1080
|
+
if (this.intentionalClose) {
|
|
1081
|
+
try {
|
|
1082
|
+
ws.close();
|
|
1083
|
+
} catch (err) {
|
|
1084
|
+
}
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
const reconnected = this.reconnectAttempts > 0;
|
|
1001
1088
|
this.playerId = String((_a = msg.playerId) != null ? _a : "");
|
|
1002
1089
|
this.players = Array.isArray(msg.players) ? msg.players : [];
|
|
1003
1090
|
this.state = (_b = msg.state) != null ? _b : null;
|
|
1091
|
+
this.connected = true;
|
|
1004
1092
|
this.reconnectAttempts = 0;
|
|
1093
|
+
this.latencyMs = Math.max(0, Date.now() - connectStartedAt);
|
|
1005
1094
|
if (!settled) {
|
|
1006
1095
|
settled = true;
|
|
1096
|
+
this.pendingConnect = null;
|
|
1007
1097
|
clearTimeout(welcomeTimer);
|
|
1008
1098
|
resolve(this);
|
|
1009
1099
|
}
|
|
1100
|
+
if (reconnected) this.emit("connection", { connected: true, reconnecting: false });
|
|
1010
1101
|
return;
|
|
1011
1102
|
}
|
|
1012
1103
|
if (msg.t === "snapshot") {
|
|
@@ -1044,25 +1135,30 @@ var RoomConnection = class {
|
|
|
1044
1135
|
if (!settled) {
|
|
1045
1136
|
settled = true;
|
|
1046
1137
|
this.terminal = true;
|
|
1138
|
+
this.pendingConnect = null;
|
|
1047
1139
|
clearTimeout(welcomeTimer);
|
|
1048
1140
|
try {
|
|
1049
1141
|
ws.close();
|
|
1050
1142
|
} catch (err) {
|
|
1051
1143
|
}
|
|
1052
|
-
reject(sdkError3(code === "unauthenticated" ? "unauthenticated" : "rejected"));
|
|
1144
|
+
reject(sdkError3(code === "unauthenticated" ? "unauthenticated" : "rejected", code));
|
|
1053
1145
|
return;
|
|
1054
1146
|
}
|
|
1055
1147
|
this.emit("error", { code });
|
|
1056
1148
|
}
|
|
1057
1149
|
};
|
|
1058
|
-
ws.onclose = () => {
|
|
1150
|
+
ws.onclose = (event) => {
|
|
1151
|
+
this.connected = false;
|
|
1152
|
+
if (event && event.code === SEAT_FULL_CLOSE_CODE) this.terminal = true;
|
|
1059
1153
|
if (!settled) {
|
|
1060
1154
|
settled = true;
|
|
1155
|
+
this.pendingConnect = null;
|
|
1061
1156
|
clearTimeout(welcomeTimer);
|
|
1062
1157
|
reject(sdkError3("error"));
|
|
1063
1158
|
return;
|
|
1064
1159
|
}
|
|
1065
1160
|
if (this.intentionalClose || this.ended || this.terminal) {
|
|
1161
|
+
this.emit("connection", { connected: false, reconnecting: false });
|
|
1066
1162
|
this.emit("close", { reconnecting: false });
|
|
1067
1163
|
return;
|
|
1068
1164
|
}
|
|
@@ -1080,11 +1176,12 @@ var RoomConnection = class {
|
|
|
1080
1176
|
return;
|
|
1081
1177
|
}
|
|
1082
1178
|
this.reconnectAttempts += 1;
|
|
1179
|
+
this.emit("connection", { connected: false, reconnecting: true });
|
|
1083
1180
|
this.emit("close", { reconnecting: true });
|
|
1084
1181
|
await new Promise((r) => setTimeout(r, 400 * this.reconnectAttempts));
|
|
1085
1182
|
if (this.intentionalClose || this.ended || this.terminal) return;
|
|
1086
1183
|
try {
|
|
1087
|
-
const grant = (_a = this.devGrant) != null ? _a : await requestTicket(this.code);
|
|
1184
|
+
const grant = (_a = this.devGrant) != null ? _a : await requestTicket({ roomId: this.code });
|
|
1088
1185
|
if (this.intentionalClose || this.ended || this.terminal) return;
|
|
1089
1186
|
await this.connect(grant);
|
|
1090
1187
|
if (this.intentionalClose) this.leave();
|
|
@@ -1093,11 +1190,16 @@ var RoomConnection = class {
|
|
|
1093
1190
|
}
|
|
1094
1191
|
}
|
|
1095
1192
|
send(input) {
|
|
1096
|
-
|
|
1193
|
+
this.trySend(input);
|
|
1194
|
+
}
|
|
1195
|
+
trySend(input) {
|
|
1196
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return false;
|
|
1097
1197
|
this.seq += 1;
|
|
1098
1198
|
try {
|
|
1099
1199
|
this.ws.send(JSON.stringify({ t: "input", seq: this.seq, data: input }));
|
|
1200
|
+
return true;
|
|
1100
1201
|
} catch (err) {
|
|
1202
|
+
return false;
|
|
1101
1203
|
}
|
|
1102
1204
|
}
|
|
1103
1205
|
on(event, handler) {
|
|
@@ -1113,6 +1215,13 @@ var RoomConnection = class {
|
|
|
1113
1215
|
leave() {
|
|
1114
1216
|
var _a;
|
|
1115
1217
|
this.intentionalClose = true;
|
|
1218
|
+
this.connected = false;
|
|
1219
|
+
if (this.pendingConnect) {
|
|
1220
|
+
const pending = this.pendingConnect;
|
|
1221
|
+
this.pendingConnect = null;
|
|
1222
|
+
clearTimeout(pending.timer);
|
|
1223
|
+
pending.reject(sdkError3("error"));
|
|
1224
|
+
}
|
|
1116
1225
|
try {
|
|
1117
1226
|
(_a = this.ws) == null ? void 0 : _a.close(1e3);
|
|
1118
1227
|
} catch (err) {
|
|
@@ -1132,17 +1241,57 @@ var RoomConnection = class {
|
|
|
1132
1241
|
}
|
|
1133
1242
|
}
|
|
1134
1243
|
};
|
|
1244
|
+
var MATCH_ATTEMPTS = 3;
|
|
1245
|
+
function normalizeWelcomeTimeoutMs(value) {
|
|
1246
|
+
if (typeof value !== "number" || !isFinite(value)) return WELCOME_TIMEOUT_MS;
|
|
1247
|
+
return Math.min(MAX_WELCOME_TIMEOUT_MS, Math.max(MIN_WELCOME_TIMEOUT_MS, Math.floor(value)));
|
|
1248
|
+
}
|
|
1249
|
+
async function matchJoin(joinData, timeoutMs) {
|
|
1250
|
+
let lastError = sdkError3("error");
|
|
1251
|
+
for (let attempt = 0; attempt < MATCH_ATTEMPTS; attempt++) {
|
|
1252
|
+
const grant = await requestTicket({ matchmake: true });
|
|
1253
|
+
if (!grant.roomId) throw sdkError3("error");
|
|
1254
|
+
try {
|
|
1255
|
+
return await new RoomConnection(grant.roomId, null, joinData, timeoutMs).connect(grant);
|
|
1256
|
+
} catch (error) {
|
|
1257
|
+
lastError = error;
|
|
1258
|
+
const detail = error == null ? void 0 : error.detail;
|
|
1259
|
+
if (detail !== "room_full" && detail !== "room_over") throw error;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
throw lastError;
|
|
1263
|
+
}
|
|
1135
1264
|
function joinRoom(options) {
|
|
1136
1265
|
const opts = options != null ? options : {};
|
|
1137
1266
|
if (typeof window === "undefined" || typeof WebSocket === "undefined") {
|
|
1138
1267
|
return Promise.reject(sdkError3("unsupported"));
|
|
1139
1268
|
}
|
|
1269
|
+
let joinData;
|
|
1270
|
+
try {
|
|
1271
|
+
joinData = serializeJoinData(opts.joinData);
|
|
1272
|
+
} catch (err) {
|
|
1273
|
+
return Promise.reject(err);
|
|
1274
|
+
}
|
|
1275
|
+
const timeoutMs = normalizeWelcomeTimeoutMs(opts.timeoutMs);
|
|
1276
|
+
if (opts.match) {
|
|
1277
|
+
if (opts.code || opts.create || opts.roomUrl || opts.ticket) {
|
|
1278
|
+
return Promise.reject(sdkError3("rejected"));
|
|
1279
|
+
}
|
|
1280
|
+
return matchJoin(joinData, timeoutMs);
|
|
1281
|
+
}
|
|
1140
1282
|
const code = opts.create || !opts.code ? generateCode() : opts.code.toUpperCase();
|
|
1141
1283
|
if (opts.roomUrl && opts.ticket) {
|
|
1142
|
-
const devGrant = {
|
|
1143
|
-
|
|
1284
|
+
const devGrant = {
|
|
1285
|
+
ticket: opts.ticket,
|
|
1286
|
+
roomUrl: opts.roomUrl,
|
|
1287
|
+
expiresAt: 0,
|
|
1288
|
+
roomId: code
|
|
1289
|
+
};
|
|
1290
|
+
return new RoomConnection(code, devGrant, joinData, timeoutMs).connect(devGrant);
|
|
1144
1291
|
}
|
|
1145
|
-
return requestTicket(
|
|
1292
|
+
return requestTicket({ roomId: code }).then(
|
|
1293
|
+
(grant) => new RoomConnection(code, null, joinData, timeoutMs).connect(grant)
|
|
1294
|
+
);
|
|
1146
1295
|
}
|
|
1147
1296
|
var multiplayer = { joinRoom };
|
|
1148
1297
|
// Annotate the CommonJS export names for ESM import in node:
|