@rpgjs/client 5.0.0-alpha.22 → 5.0.0-alpha.24

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 (134) hide show
  1. package/dist/Game/Object.d.ts +2 -0
  2. package/dist/RpgClientEngine.d.ts +115 -9
  3. package/dist/components/gui/mobile/index.d.ts +8 -0
  4. package/dist/components/prebuilt/index.d.ts +1 -0
  5. package/dist/index.d.ts +5 -0
  6. package/dist/index.js +13 -8
  7. package/dist/index.js.map +1 -1
  8. package/dist/index10.js +1 -1
  9. package/dist/index11.js +6 -5
  10. package/dist/index11.js.map +1 -1
  11. package/dist/index12.js +2 -2
  12. package/dist/index13.js +102 -10
  13. package/dist/index13.js.map +1 -1
  14. package/dist/index14.js +67 -9
  15. package/dist/index14.js.map +1 -1
  16. package/dist/index15.js +10 -263
  17. package/dist/index15.js.map +1 -1
  18. package/dist/index16.js +9 -97
  19. package/dist/index16.js.map +1 -1
  20. package/dist/index17.js +300 -89
  21. package/dist/index17.js.map +1 -1
  22. package/dist/index18.js +63 -80
  23. package/dist/index18.js.map +1 -1
  24. package/dist/index19.js +96 -348
  25. package/dist/index19.js.map +1 -1
  26. package/dist/index2.js +176 -24
  27. package/dist/index2.js.map +1 -1
  28. package/dist/index20.js +360 -17
  29. package/dist/index20.js.map +1 -1
  30. package/dist/index21.js +19 -50
  31. package/dist/index21.js.map +1 -1
  32. package/dist/index22.js +212 -5
  33. package/dist/index22.js.map +1 -1
  34. package/dist/index23.js +6 -395
  35. package/dist/index23.js.map +1 -1
  36. package/dist/index24.js +4 -39
  37. package/dist/index24.js.map +1 -1
  38. package/dist/index25.js +19 -20
  39. package/dist/index25.js.map +1 -1
  40. package/dist/index26.js +43 -2624
  41. package/dist/index26.js.map +1 -1
  42. package/dist/index27.js +5 -110
  43. package/dist/index27.js.map +1 -1
  44. package/dist/index28.js +394 -65
  45. package/dist/index28.js.map +1 -1
  46. package/dist/index29.js +40 -15
  47. package/dist/index29.js.map +1 -1
  48. package/dist/index3.js +3 -3
  49. package/dist/index30.js +21 -23
  50. package/dist/index30.js.map +1 -1
  51. package/dist/index31.js +2624 -86
  52. package/dist/index31.js.map +1 -1
  53. package/dist/index32.js +107 -34
  54. package/dist/index32.js.map +1 -1
  55. package/dist/index33.js +69 -22
  56. package/dist/index33.js.map +1 -1
  57. package/dist/index34.js +19 -3
  58. package/dist/index34.js.map +1 -1
  59. package/dist/index35.js +21 -329
  60. package/dist/index35.js.map +1 -1
  61. package/dist/index36.js +91 -30
  62. package/dist/index36.js.map +1 -1
  63. package/dist/index37.js +37 -7
  64. package/dist/index37.js.map +1 -1
  65. package/dist/index38.js +22 -9
  66. package/dist/index38.js.map +1 -1
  67. package/dist/index39.js +139 -10
  68. package/dist/index39.js.map +1 -1
  69. package/dist/index4.js +3 -3
  70. package/dist/index40.js +16 -6
  71. package/dist/index40.js.map +1 -1
  72. package/dist/index41.js +1 -325
  73. package/dist/index41.js.map +1 -1
  74. package/dist/index42.js +530 -3680
  75. package/dist/index42.js.map +1 -1
  76. package/dist/index43.js +24 -67
  77. package/dist/index43.js.map +1 -1
  78. package/dist/index44.js +9 -184
  79. package/dist/index44.js.map +1 -1
  80. package/dist/index45.js +6 -503
  81. package/dist/index45.js.map +1 -1
  82. package/dist/index46.js +325 -2
  83. package/dist/index46.js.map +1 -1
  84. package/dist/index47.js +3687 -17
  85. package/dist/index47.js.map +1 -1
  86. package/dist/index48.js +69 -202
  87. package/dist/index48.js.map +1 -1
  88. package/dist/index49.js +182 -7
  89. package/dist/index49.js.map +1 -1
  90. package/dist/index5.js +1 -1
  91. package/dist/index50.js +497 -106
  92. package/dist/index50.js.map +1 -1
  93. package/dist/index51.js +48 -130
  94. package/dist/index51.js.map +1 -1
  95. package/dist/index52.js +17 -134
  96. package/dist/index52.js.map +1 -1
  97. package/dist/index53.js +3 -109
  98. package/dist/index53.js.map +1 -1
  99. package/dist/index54.js +9 -138
  100. package/dist/index54.js.map +1 -1
  101. package/dist/index55.js +111 -7
  102. package/dist/index55.js.map +1 -1
  103. package/dist/index56.js +130 -48
  104. package/dist/index56.js.map +1 -1
  105. package/dist/index57.js +137 -0
  106. package/dist/index57.js.map +1 -0
  107. package/dist/index58.js +112 -0
  108. package/dist/index58.js.map +1 -0
  109. package/dist/index59.js +9 -0
  110. package/dist/index59.js.map +1 -0
  111. package/dist/index6.js +1 -1
  112. package/dist/index7.js +1 -1
  113. package/dist/index8.js +17 -2
  114. package/dist/index8.js.map +1 -1
  115. package/dist/index9.js +10 -27
  116. package/dist/index9.js.map +1 -1
  117. package/dist/services/keyboardControls.d.ts +1 -2
  118. package/dist/services/mmorpg.d.ts +1 -1
  119. package/dist/services/standalone.d.ts +1 -1
  120. package/package.json +9 -9
  121. package/src/Game/Object.ts +8 -0
  122. package/src/Gui/Gui.ts +4 -31
  123. package/src/RpgClientEngine.ts +193 -20
  124. package/src/components/character.ce +146 -9
  125. package/src/components/gui/mobile/index.ts +24 -0
  126. package/src/components/gui/mobile/mobile.ce +80 -0
  127. package/src/components/prebuilt/index.ts +1 -0
  128. package/src/components/prebuilt/light-halo.ce +148 -0
  129. package/src/components/scenes/canvas.ce +2 -2
  130. package/src/components/scenes/event-layer.ce +1 -0
  131. package/src/components/scenes/transition.ce +60 -0
  132. package/src/index.ts +6 -1
  133. package/src/module.ts +15 -0
  134. package/src/services/keyboardControls.ts +1 -1
package/dist/index26.js CHANGED
@@ -1,2633 +1,52 @@
1
- import { dset } from './index39.js';
2
- import * as external from './index40.js';
3
- import { createStatesSnapshot, syncClass, generateShortUUID as generateShortUUID$1, load, id, sync, persist, getByPath, DELETE_TOKEN } from './index23.js';
4
- import { signal } from './index41.js';
5
- import { object as objectType, number as numberType, boolean as booleanType, enum as enumType, string as stringType } from './index42.js';
6
-
7
- var __defProp = Object.defineProperty;
8
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
-
10
- // src/decorators.ts
11
- function Action(name, bodyValidation) {
12
- return function(target, propertyKey) {
13
- if (!target.constructor._actionMetadata) {
14
- target.constructor._actionMetadata = /* @__PURE__ */ new Map();
15
- }
16
- target.constructor._actionMetadata.set(name, {
17
- key: propertyKey,
18
- bodyValidation
19
- });
20
- };
21
- }
22
- __name(Action, "Action");
23
- function Request2(options, bodyValidation) {
24
- return function(target, propertyKey) {
25
- if (!target.constructor._requestMetadata) {
26
- target.constructor._requestMetadata = /* @__PURE__ */ new Map();
27
- }
28
- const path = options.path.startsWith("/") ? options.path : `/${options.path}`;
29
- const method = options.method || "GET";
30
- const routeKey = `${method}:${path}`;
31
- target.constructor._requestMetadata.set(routeKey, {
32
- key: propertyKey,
33
- path,
34
- method,
35
- bodyValidation
36
- });
37
- };
38
- }
39
- __name(Request2, "Request");
40
- function Room(options) {
41
- return function(target) {
42
- target.path = options.path;
43
- target.prototype.maxUsers = options.maxUsers;
44
- target.prototype.throttleStorage = options.throttleStorage;
45
- target.prototype.throttleSync = options.throttleSync;
46
- target.prototype.sessionExpiryTime = options.sessionExpiryTime ?? 5 * 60 * 1e3;
47
- if (options.guards) {
48
- target["_roomGuards"] = options.guards;
49
- }
50
- };
51
- }
52
- __name(Room, "Room");
53
- function RoomGuard(guards) {
54
- return function(target) {
55
- target["_roomGuards"] = guards;
56
- };
57
- }
58
- __name(RoomGuard, "RoomGuard");
59
- function Guard(guards) {
60
- return function(target, propertyKey, descriptor) {
61
- if (!target.constructor["_actionGuards"]) {
62
- target.constructor["_actionGuards"] = /* @__PURE__ */ new Map();
63
- }
64
- if (!Array.isArray(guards)) {
65
- guards = [
66
- guards
67
- ];
68
- }
69
- target.constructor["_actionGuards"].set(propertyKey, guards);
70
- };
71
- }
72
- __name(Guard, "Guard");
73
-
74
- // ../sync/src/utils.ts
75
- function generateShortUUID() {
76
- const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
77
- let uuid = "";
78
- for (let i = 0; i < 8; i++) {
79
- const randomIndex = Math.floor(Math.random() * chars.length);
80
- uuid += chars[randomIndex];
81
- }
82
- return uuid;
83
- }
84
- __name(generateShortUUID, "generateShortUUID");
85
-
86
- // src/storage.ts
87
- var Storage = class {
88
- static {
89
- __name(this, "Storage");
90
- }
91
- memory = /* @__PURE__ */ new Map();
92
- async put(key, value) {
93
- this.memory.set(key, value);
94
- }
95
- async get(key) {
96
- return this.memory.get(key);
97
- }
98
- async delete(key) {
99
- this.memory.delete(key);
100
- }
101
- async list() {
102
- return this.memory;
103
- }
104
- };
105
- function isPromise(value) {
106
- return value instanceof Promise;
107
- }
108
- __name(isPromise, "isPromise");
109
- async function awaitReturn(val) {
110
- return isPromise(val) ? await val : val;
111
- }
112
- __name(awaitReturn, "awaitReturn");
113
- function isClass(obj) {
114
- return typeof obj === "function" && obj.prototype && obj.prototype.constructor === obj;
115
- }
116
- __name(isClass, "isClass");
117
- function throttle(func, wait) {
118
- let timeout = null;
119
- let lastArgs = null;
120
- return function(...args) {
121
- if (!timeout) {
122
- func(...args);
123
- timeout = setTimeout(() => {
124
- if (lastArgs) {
125
- func(...lastArgs);
126
- lastArgs = null;
127
- }
128
- timeout = null;
129
- }, wait);
130
- } else {
131
- lastArgs = args;
132
- }
133
- };
134
- }
135
- __name(throttle, "throttle");
136
- function extractParams(pattern, str) {
137
- const regexPattern = pattern.replace(/{(\w+)}/g, "(?<$1>[\\w-]+)");
138
- const regex = new RegExp(`^${regexPattern}$`);
139
- const match = regex.exec(str);
140
- if (match && match.groups) {
141
- return match.groups;
142
- } else if (pattern === str) {
143
- return {};
144
- } else {
145
- return null;
146
- }
147
- }
148
- __name(extractParams, "extractParams");
149
- function dremove(obj, keys) {
150
- if (typeof keys === "string") {
151
- keys = keys.split(".");
152
- }
153
- let i = 0;
154
- const l = keys.length;
155
- let t = obj;
156
- let k;
157
- while (i < l - 1) {
158
- k = keys[i++];
159
- if (k === "__proto__" || k === "constructor" || k === "prototype") return;
160
- if (typeof t[k] !== "object" || t[k] === null) return;
161
- t = t[k];
162
- }
163
- k = keys[i];
164
- if (t && typeof t === "object" && !(k === "__proto__" || k === "constructor" || k === "prototype")) {
165
- delete t[k];
166
- }
167
- }
168
- __name(dremove, "dremove");
169
- function buildObject(valuesMap, allMemory) {
170
- let memoryObj = {};
171
- for (let path of valuesMap.keys()) {
172
- const value = valuesMap.get(path);
173
- dset(memoryObj, path, value);
174
- if (value === "$delete") {
175
- dremove(allMemory, path);
176
- } else {
177
- dset(allMemory, path, value);
178
- }
179
- }
180
- return memoryObj;
181
- }
182
- __name(buildObject, "buildObject");
183
- function response(status, body) {
184
- return new Response(JSON.stringify(body), {
185
- status,
186
- headers: {
187
- "Content-Type": "application/json"
188
- }
189
- });
190
- }
191
- __name(response, "response");
192
-
193
- // src/request/response.ts
194
- var ServerResponse = class {
195
- static {
196
- __name(this, "ServerResponse");
197
- }
198
- interceptors;
199
- statusCode = 200;
200
- responseBody = {};
201
- responseHeaders = {
202
- "Content-Type": "application/json"
203
- };
204
- /**
205
- * Creates a new ServerResponse instance
206
- * @param interceptors Array of interceptor functions that can modify the response
207
- */
208
- constructor(interceptors = []) {
209
- this.interceptors = interceptors;
210
- }
211
- /**
212
- * Sets the status code for the response
213
- * @param code HTTP status code
214
- * @returns this instance for chaining
215
- */
216
- status(code) {
217
- this.statusCode = code;
218
- return this;
219
- }
220
- /**
221
- * Sets the response body and returns this instance (chainable method)
222
- *
223
- * @param body Response body
224
- * @returns this instance for chaining
225
- */
226
- body(body) {
227
- this.responseBody = body;
228
- return this;
229
- }
230
- /**
231
- * Adds a header to the response
232
- * @param name Header name
233
- * @param value Header value
234
- * @returns this instance for chaining
235
- */
236
- header(name, value) {
237
- this.responseHeaders[name] = value;
238
- return this;
239
- }
240
- /**
241
- * Adds multiple headers to the response
242
- * @param headers Object containing headers
243
- * @returns this instance for chaining
244
- */
245
- setHeaders(headers) {
246
- this.responseHeaders = {
247
- ...this.responseHeaders,
248
- ...headers
249
- };
250
- return this;
251
- }
252
- /**
253
- * Add an interceptor to the chain
254
- * @param interceptor Function that takes a Response and returns a modified Response
255
- * @returns this instance for chaining
256
- */
257
- use(interceptor) {
258
- this.interceptors.push(interceptor);
259
- return this;
260
- }
261
- /**
262
- * Builds and returns the Response object after applying all interceptors
263
- * @returns Promise<Response> The final Response object
264
- * @private Internal method used by terminal methods
265
- */
266
- async buildResponse() {
267
- let response2 = new Response(JSON.stringify(this.responseBody), {
268
- status: this.statusCode,
269
- headers: this.responseHeaders
270
- });
271
- for (const interceptor of this.interceptors) {
272
- try {
273
- const interceptedResponse = interceptor(response2);
274
- if (interceptedResponse instanceof Promise) {
275
- response2 = await interceptedResponse;
276
- } else {
277
- response2 = interceptedResponse;
278
- }
279
- } catch (error) {
280
- console.error("Error in interceptor:", error);
281
- }
282
- }
283
- return response2;
284
- }
285
- /**
286
- * Sets the response body to the JSON-stringified version of the provided value
287
- * and sends the response (terminal method)
288
- *
289
- * @param body Response body to be JSON stringified
290
- * @returns Promise<Response> The final Response object
291
- */
292
- async json(body) {
293
- this.responseBody = body;
294
- this.responseHeaders["Content-Type"] = "application/json";
295
- return this.buildResponse();
296
- }
297
- /**
298
- * Sends the response with the current configuration (terminal method)
299
- *
300
- * @param body Optional body to set before sending
301
- * @returns Promise<Response> The final Response object
302
- */
303
- async send(body) {
304
- if (body !== void 0) {
305
- this.responseBody = body;
306
- }
307
- return this.buildResponse();
308
- }
309
- /**
310
- * Sends a plain text response (terminal method)
311
- *
312
- * @param text Text to send
313
- * @returns Promise<Response> The final Response object
314
- */
315
- async text(text) {
316
- this.responseBody = text;
317
- this.responseHeaders["Content-Type"] = "text/plain";
318
- let response2 = new Response(text, {
319
- status: this.statusCode,
320
- headers: this.responseHeaders
321
- });
322
- for (const interceptor of this.interceptors) {
323
- try {
324
- const interceptedResponse = interceptor(response2);
325
- if (interceptedResponse instanceof Promise) {
326
- response2 = await interceptedResponse;
327
- } else {
328
- response2 = interceptedResponse;
329
- }
330
- } catch (error) {
331
- console.error("Error in interceptor:", error);
332
- }
333
- }
334
- return response2;
335
- }
336
- /**
337
- * Redirects to the specified URL (terminal method)
338
- *
339
- * @param url URL to redirect to
340
- * @param statusCode HTTP status code (default: 302)
341
- * @returns Promise<Response> The final Response object
342
- */
343
- async redirect(url, statusCode = 302) {
344
- this.statusCode = statusCode;
345
- this.responseHeaders["Location"] = url;
346
- return this.buildResponse();
347
- }
348
- /**
349
- * Creates a success response with status 200
350
- * @param body Response body
351
- * @returns Promise<Response> The final Response object
352
- */
353
- async success(body = {}) {
354
- return this.status(200).json(body);
355
- }
356
- /**
357
- * Creates an error response with status 400
358
- * @param message Error message
359
- * @param details Additional error details
360
- * @returns Promise<Response> The final Response object
361
- */
362
- async badRequest(message, details = {}) {
363
- return this.status(400).json({
364
- error: message,
365
- ...details
366
- });
367
- }
368
- /**
369
- * Creates an error response with status 403
370
- * @param message Error message
371
- * @returns Promise<Response> The final Response object
372
- */
373
- async notPermitted(message = "Not permitted") {
374
- return this.status(403).json({
375
- error: message
376
- });
377
- }
378
- /**
379
- * Creates an error response with status 401
380
- * @param message Error message
381
- * @returns Promise<Response> The final Response object
382
- */
383
- async unauthorized(message = "Unauthorized") {
384
- return this.status(401).json({
385
- error: message
386
- });
387
- }
388
- /**
389
- * Creates an error response with status 404
390
- * @param message Error message
391
- * @returns Promise<Response> The final Response object
392
- */
393
- async notFound(message = "Not found") {
394
- return this.status(404).json({
395
- error: message
396
- });
397
- }
398
- /**
399
- * Creates an error response with status 500
400
- * @param message Error message
401
- * @returns Promise<Response> The final Response object
402
- */
403
- async serverError(message = "Internal Server Error") {
404
- return this.status(500).json({
405
- error: message
406
- });
407
- }
408
- };
409
-
410
- // src/request/cors.ts
411
- function cors(res, options = {}) {
412
- const newHeaders = new Headers(res.headers);
413
- const requestOrigin = options.origin || "*";
414
- newHeaders.set("Access-Control-Allow-Origin", requestOrigin);
415
- if (options.credentials) {
416
- newHeaders.set("Access-Control-Allow-Credentials", "true");
417
- }
418
- if (options.exposedHeaders && options.exposedHeaders.length) {
419
- newHeaders.set("Access-Control-Expose-Headers", options.exposedHeaders.join(", "));
420
- }
421
- if (options.methods && options.methods.length) {
422
- newHeaders.set("Access-Control-Allow-Methods", options.methods.join(", "));
423
- } else {
424
- newHeaders.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
425
- }
426
- if (options.allowedHeaders && options.allowedHeaders.length) {
427
- newHeaders.set("Access-Control-Allow-Headers", options.allowedHeaders.join(", "));
428
- } else {
429
- newHeaders.set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
430
- }
431
- if (options.maxAge) {
432
- newHeaders.set("Access-Control-Max-Age", options.maxAge.toString());
433
- } else {
434
- newHeaders.set("Access-Control-Max-Age", "86400");
435
- }
436
- return new Response(res.body, {
437
- status: res.status,
438
- headers: newHeaders
439
- });
440
- }
441
- __name(cors, "cors");
442
- function createCorsInterceptor(options = {}) {
443
- return (res) => cors(res, options);
444
- }
445
- __name(createCorsInterceptor, "createCorsInterceptor");
446
-
447
- // src/server.ts
448
- var Message = external.object({
449
- action: external.string(),
450
- value: external.any()
1
+ import { useProps, useDefineProps, computed, effect, h, Canvas, loop, Viewport, cond, Container } from 'canvasengine';
2
+ import { inject } from './index6.js';
3
+ import { RpgClientEngine } from './index2.js';
4
+ import component$1 from './index43.js';
5
+ import { RpgGui } from './index9.js';
6
+
7
+ function component($$props) {
8
+ useProps($$props);
9
+ useDefineProps($$props);
10
+ var __assign = (this && this.__assign) || function () {
11
+ __assign = Object.assign || function(t) {
12
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
13
+ s = arguments[i];
14
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
15
+ t[p] = s[p];
16
+ }
17
+ return t;
18
+ };
19
+ return __assign.apply(this, arguments);
20
+ };
21
+ var engine = inject(RpgClientEngine);
22
+ var guiService = inject(RpgGui);
23
+ var sceneData = engine.sceneMap.data;
24
+ var gui = computed(function () {
25
+ return Object.values(guiService.gui()).filter(function (gui) { return !gui.attachToSprite; });
451
26
  });
452
- var Server = class {
453
- static {
454
- __name(this, "Server");
455
- }
456
- room;
457
- subRoom;
458
- rooms;
459
- /**
460
- * @constructor
461
- * @param {Party.Room} room - The room object representing the current game or application instance.
462
- *
463
- * @example
464
- * ```typescript
465
- * const server = new MyServer(new ServerIo("game"));
466
- * ```
467
- */
468
- constructor(room) {
469
- this.room = room;
470
- this.subRoom = null;
471
- this.rooms = [];
472
- }
473
- /**
474
- * @readonly
475
- * @property {boolean} isHibernate - Indicates whether the server is in hibernate mode.
476
- *
477
- * @example
478
- * ```typescript
479
- * if (!server.isHibernate) {
480
- * console.log("Server is active");
481
- * }
482
- * ```
483
- */
484
- get isHibernate() {
485
- return !!this["options"]?.hibernate;
486
- }
487
- get roomStorage() {
488
- return this.room.storage;
489
- }
490
- async send(conn, obj, subRoom) {
491
- obj = structuredClone(obj);
492
- if (subRoom.interceptorPacket) {
493
- const signal2 = this.getUsersProperty(subRoom);
494
- const { publicId } = conn.state;
495
- const user = signal2?.()[publicId];
496
- obj = await awaitReturn(subRoom["interceptorPacket"]?.(user, obj, conn));
497
- if (obj === null) return;
498
- }
499
- conn.send(JSON.stringify(obj));
500
- }
501
- broadcast(obj, subRoom) {
502
- for (let conn of this.room.getConnections()) {
503
- this.send(conn, obj, subRoom);
504
- }
505
- }
506
- /**
507
- * @method onStart
508
- * @async
509
- * @description Initializes the server and creates the initial room if not in hibernate mode.
510
- * @returns {Promise<void>}
511
- *
512
- * @example
513
- * ```typescript
514
- * async function initServer() {
515
- * await server.onStart();
516
- * console.log("Server started");
517
- * }
518
- * ```
519
- */
520
- async onStart() {
521
- if (!this.isHibernate) {
522
- this.subRoom = await this.createRoom();
523
- }
524
- }
525
- async runGarbageCollector() {
526
- await this.garbageCollector({
527
- sessionExpiryTime: -1
528
- });
529
- }
530
- async garbageCollector(options) {
531
- const subRoom = await this.getSubRoom();
532
- if (!subRoom) return;
533
- const activeConnections = [
534
- ...this.room.getConnections()
535
- ];
536
- const activePrivateIds = new Set(activeConnections.map((conn) => conn.id));
537
- try {
538
- const sessions = await this.room.storage.list();
539
- const users = this.getUsersProperty(subRoom);
540
- const usersPropName = this.getUsersPropName(subRoom);
541
- const validPublicIds = /* @__PURE__ */ new Set();
542
- const expiredPublicIds = /* @__PURE__ */ new Set();
543
- const SESSION_EXPIRY_TIME = options.sessionExpiryTime;
544
- const now = Date.now();
545
- for (const [key, session] of sessions) {
546
- if (!key.startsWith("session:")) continue;
547
- const privateId = key.replace("session:", "");
548
- const typedSession = session;
549
- if (!activePrivateIds.has(privateId) && !typedSession.connected && now - typedSession.created > SESSION_EXPIRY_TIME) {
550
- await this.deleteSession(privateId);
551
- expiredPublicIds.add(typedSession.publicId);
552
- } else if (typedSession && typedSession.publicId) {
553
- validPublicIds.add(typedSession.publicId);
554
- }
555
- }
556
- if (users && usersPropName) {
557
- const currentUsers = users();
558
- for (const publicId in currentUsers) {
559
- if (expiredPublicIds.has(publicId) && !validPublicIds.has(publicId)) {
560
- delete currentUsers[publicId];
561
- await this.room.storage.delete(`${usersPropName}.${publicId}`);
562
- }
563
- }
564
- }
565
- } catch (error) {
566
- console.error("Error in garbage collector:", error);
567
- }
568
- }
569
- /**
570
- * @method createRoom
571
- * @private
572
- * @async
573
- * @param {CreateRoomOptions} [options={}] - Options for creating the room.
574
- * @returns {Promise<Object>} The created room instance.
575
- *
576
- * @example
577
- * ```typescript
578
- * // This method is private and called internally
579
- * async function internalCreateRoom() {
580
- * const room = await this.createRoom({ getMemoryAll: true });
581
- * console.log("Room created:", room);
582
- * }
583
- * ```
584
- */
585
- async createRoom(options = {}) {
586
- let instance;
587
- let init = true;
588
- let initPersist = true;
589
- for (let room of this.rooms) {
590
- const params = extractParams(room.path, this.room.id);
591
- if (params) {
592
- instance = new room(this.room, params);
593
- break;
594
- }
595
- }
596
- if (!instance) {
597
- return null;
598
- }
599
- const loadMemory = /* @__PURE__ */ __name(async () => {
600
- const root = await this.room.storage.get(".");
601
- const memory = await this.room.storage.list();
602
- const tmpObject = root || {};
603
- for (let [key, value] of memory) {
604
- if (key.startsWith("session:")) {
605
- continue;
606
- }
607
- if (key == ".") {
608
- continue;
609
- }
610
- dset(tmpObject, key, value);
611
- }
612
- load(instance, tmpObject, true);
613
- }, "loadMemory");
614
- instance.$memoryAll = {};
615
- instance.$send = (conn, obj) => {
616
- return this.send(conn, obj, instance);
617
- };
618
- instance.$broadcast = (obj) => {
619
- return this.broadcast(obj, instance);
620
- };
621
- instance.$sessionTransfer = async (conn, targetRoomId) => {
622
- let user;
623
- const signal2 = this.getUsersProperty(instance);
624
- if (!signal2) {
625
- console.error("[sessionTransfer] `users` property not defined in the room.");
626
- return null;
627
- }
628
- const { publicId } = conn.state;
629
- user = signal2()[publicId];
630
- if (!user) {
631
- console.error(`[sessionTransfer] User with publicId ${publicId} not found.`);
632
- return null;
633
- }
634
- const sessions = await this.room.storage.list();
635
- let userSession = null;
636
- let privateId = null;
637
- for (const [key, session] of sessions) {
638
- if (key.startsWith("session:") && session.publicId === publicId) {
639
- userSession = session;
640
- privateId = key.replace("session:", "");
641
- break;
642
- }
643
- }
644
- if (!userSession || !privateId) {
645
- console.error(`[sessionTransfer] Session for publicId ${publicId} not found.`);
646
- return null;
647
- }
648
- const usersPropName = this.getUsersPropName(instance);
649
- if (!usersPropName) {
650
- console.error("[sessionTransfer] `users` property not defined in the room.");
651
- return null;
652
- }
653
- const userSnapshot = createStatesSnapshot(user);
654
- const transferData = {
655
- privateId,
656
- userSnapshot,
657
- sessionState: userSession.state,
658
- publicId
659
- };
660
- try {
661
- const targetRoomParty = await this.room.context.parties.main.get(targetRoomId);
662
- const response2 = await targetRoomParty.fetch("/session-transfer", {
663
- method: "POST",
664
- body: JSON.stringify(transferData),
665
- headers: {
666
- "Content-Type": "application/json"
667
- }
668
- });
669
- if (!response2.ok) {
670
- throw new Error(`Transfer request failed: ${await response2.text()}`);
671
- }
672
- const { transferToken } = await response2.json();
673
- return transferToken;
674
- } catch (error) {
675
- console.error(`[sessionTransfer] Failed to transfer session to room ${targetRoomId}:`, error);
676
- return null;
677
- }
678
- };
679
- const syncCb = /* @__PURE__ */ __name((values) => {
680
- if (options.getMemoryAll) {
681
- buildObject(values, instance.$memoryAll);
682
- }
683
- if (init && this.isHibernate) {
684
- init = false;
685
- return;
686
- }
687
- const packet = buildObject(values, instance.$memoryAll);
688
- this.broadcast({
689
- type: "sync",
690
- value: packet
691
- }, instance);
692
- values.clear();
693
- }, "syncCb");
694
- const persistCb = /* @__PURE__ */ __name(async (values) => {
695
- if (initPersist) {
696
- values.clear();
697
- return;
698
- }
699
- for (let [path, value] of values) {
700
- const _instance = path == "." ? instance : getByPath(instance, path);
701
- const itemValue = createStatesSnapshot(_instance);
702
- if (value == DELETE_TOKEN) {
703
- await this.room.storage.delete(path);
704
- } else {
705
- await this.room.storage.put(path, itemValue);
706
- }
707
- }
708
- values.clear();
709
- }, "persistCb");
710
- syncClass(instance, {
711
- onSync: throttle(syncCb, instance["throttleSync"] ?? 500),
712
- onPersist: throttle(persistCb, instance["throttleStorage"] ?? 2e3)
713
- });
714
- await loadMemory();
715
- initPersist = false;
716
- return instance;
717
- }
718
- /**
719
- * @method getSubRoom
720
- * @private
721
- * @async
722
- * @param {Object} [options={}] - Options for getting the sub-room.
723
- * @returns {Promise<Object>} The sub-room instance.
724
- *
725
- * @example
726
- * ```typescript
727
- * // This method is private and called internally
728
- * async function internalGetSubRoom() {
729
- * const subRoom = await this.getSubRoom();
730
- * console.log("Sub-room retrieved:", subRoom);
731
- * }
732
- * ```
733
- */
734
- async getSubRoom(options = {}) {
735
- let subRoom;
736
- if (this.isHibernate) {
737
- subRoom = await this.createRoom(options);
738
- } else {
739
- subRoom = this.subRoom;
740
- }
741
- return subRoom;
742
- }
743
- /**
744
- * @method getUsersProperty
745
- * @private
746
- * @param {Object} subRoom - The sub-room instance.
747
- * @returns {Object|null} The users property of the sub-room, or null if not found.
748
- *
749
- * @example
750
- * ```typescript
751
- * // This method is private and called internally
752
- * function internalGetUsers(subRoom) {
753
- * const users = this.getUsersProperty(subRoom);
754
- * console.log("Users:", users);
755
- * }
756
- * ```
757
- */
758
- getUsersProperty(subRoom) {
759
- const meta = subRoom.constructor["_propertyMetadata"];
760
- const propId = meta?.get("users");
761
- if (propId) {
762
- return subRoom[propId];
763
- }
764
- return null;
765
- }
766
- getUsersPropName(subRoom) {
767
- if (!subRoom) return null;
768
- const metadata = subRoom.constructor._propertyMetadata;
769
- if (!metadata) return null;
770
- return metadata.get("users");
771
- }
772
- /**
773
- * Retrieves the connection status property from a user object.
774
- *
775
- * @param {any} user - The user object to get the connection property from.
776
- * @returns {Function|null} - The connection property signal function or null if not found.
777
- * @private
778
- */
779
- getUserConnectionProperty(user) {
780
- if (!user) return null;
781
- const metadata = user.constructor._propertyMetadata;
782
- if (!metadata) return null;
783
- const connectedPropName = metadata.get("connected");
784
- if (!connectedPropName) return null;
785
- return user[connectedPropName];
786
- }
787
- /**
788
- * Updates a user's connection status in the signal.
789
- *
790
- * @param {any} user - The user object to update.
791
- * @param {boolean} isConnected - The new connection status.
792
- * @returns {boolean} - Whether the update was successful.
793
- * @private
794
- */
795
- updateUserConnectionStatus(user, isConnected) {
796
- const connectionSignal = this.getUserConnectionProperty(user);
797
- if (connectionSignal) {
798
- connectionSignal.set(isConnected);
799
- return true;
800
- }
801
- return false;
802
- }
803
- /**
804
- * @method getSession
805
- * @private
806
- * @param {string} privateId - The private ID of the session.
807
- * @returns {Promise<Object|null>} The session object, or null if not found.
808
- *
809
- * @example
810
- * ```typescript
811
- * const session = await server.getSession("privateId");
812
- * console.log(session);
813
- * ```
814
- */
815
- async getSession(privateId) {
816
- if (!privateId) return null;
817
- try {
818
- const session = await this.room.storage.get(`session:${privateId}`);
819
- return session;
820
- } catch (e) {
821
- return null;
822
- }
823
- }
824
- async saveSession(privateId, data) {
825
- const sessionData = {
826
- ...data,
827
- created: data.created || Date.now(),
828
- connected: data.connected !== void 0 ? data.connected : true
829
- };
830
- await this.room.storage.put(`session:${privateId}`, sessionData);
831
- }
832
- async updateSessionConnection(privateId, connected) {
833
- const session = await this.getSession(privateId);
834
- if (session) {
835
- await this.saveSession(privateId, {
836
- ...session,
837
- connected
838
- });
839
- }
840
- }
841
- /**
842
- * @method deleteSession
843
- * @private
844
- * @param {string} privateId - The private ID of the session to delete.
845
- * @returns {Promise<void>}
846
- *
847
- * @example
848
- * ```typescript
849
- * await server.deleteSession("privateId");
850
- * ```
851
- */
852
- async deleteSession(privateId) {
853
- await this.room.storage.delete(`session:${privateId}`);
854
- }
855
- async onConnectClient(conn, ctx) {
856
- const subRoom = await this.getSubRoom({
857
- getMemoryAll: true
858
- });
859
- if (!subRoom) {
860
- conn.close();
861
- return;
862
- }
863
- const sessionExpiryTime = subRoom.constructor.sessionExpiryTime;
864
- await this.garbageCollector({
865
- sessionExpiryTime
866
- });
867
- const roomGuards = subRoom.constructor["_roomGuards"] || [];
868
- for (const guard of roomGuards) {
869
- const isAuthorized = await guard(conn, ctx, this.room);
870
- if (!isAuthorized) {
871
- conn.close();
872
- return;
873
- }
874
- }
875
- let transferToken = null;
876
- if (ctx.request?.url) {
877
- const url = new URL(ctx.request.url);
878
- transferToken = url.searchParams.get("transferToken");
879
- }
880
- let transferData = null;
881
- if (transferToken) {
882
- transferData = await this.room.storage.get(`transfer:${transferToken}`);
883
- if (transferData) {
884
- await this.room.storage.delete(`transfer:${transferToken}`);
885
- }
886
- }
887
- const existingSession = await this.getSession(conn.id);
888
- const publicId = existingSession?.publicId || transferData?.publicId || generateShortUUID$1();
889
- let user = null;
890
- const signal2 = this.getUsersProperty(subRoom);
891
- const usersPropName = this.getUsersPropName(subRoom);
892
- if (signal2) {
893
- const { classType } = signal2.options;
894
- if (!existingSession?.publicId) {
895
- if (transferData?.restored && signal2()[publicId]) {
896
- user = signal2()[publicId];
897
- } else {
898
- user = isClass(classType) ? new classType() : classType(conn, ctx);
899
- signal2()[publicId] = user;
900
- const snapshot = createStatesSnapshot(user);
901
- this.room.storage.put(`${usersPropName}.${publicId}`, snapshot);
902
- }
903
- } else {
904
- user = signal2()[existingSession.publicId];
905
- }
906
- if (!existingSession) {
907
- const sessionPrivateId = transferData?.privateId || conn.id;
908
- await this.saveSession(sessionPrivateId, {
909
- publicId
910
- });
911
- } else {
912
- await this.updateSessionConnection(conn.id, true);
913
- }
914
- }
915
- this.updateUserConnectionStatus(user, true);
916
- conn.setState({
917
- ...conn.state,
918
- publicId
919
- });
920
- await awaitReturn(subRoom["onJoin"]?.(user, conn, ctx));
921
- this.send(conn, {
922
- type: "sync",
923
- value: {
924
- pId: publicId,
925
- ...subRoom.$memoryAll
926
- }
927
- }, subRoom);
928
- }
929
- /**
930
- * @method onConnect
931
- * @async
932
- * @param {Party.Connection} conn - The connection object for the new user.
933
- * @param {Party.ConnectionContext} ctx - The context of the connection.
934
- * @description Handles a new user connection, creates a user object, and sends initial sync data.
935
- * @returns {Promise<void>}
936
- *
937
- * @example
938
- * ```typescript
939
- * server.onConnect = async (conn, ctx) => {
940
- * await server.onConnect(conn, ctx);
941
- * console.log("New user connected:", conn.id);
942
- * };
943
- * ```
944
- */
945
- async onConnect(conn, ctx) {
946
- if (ctx.request?.headers.has("x-shard-id")) {
947
- this.onConnectShard(conn, ctx);
948
- } else {
949
- await this.onConnectClient(conn, ctx);
950
- }
951
- }
952
- /**
953
- * @method onConnectShard
954
- * @private
955
- * @param {Party.Connection} conn - The connection object for the new shard.
956
- * @param {Party.ConnectionContext} ctx - The context of the shard connection.
957
- * @description Handles a new shard connection, setting up the necessary state.
958
- * @returns {void}
959
- */
960
- onConnectShard(conn, ctx) {
961
- const shardId = ctx.request?.headers.get("x-shard-id") || "unknown-shard";
962
- conn.setState({
963
- shard: true,
964
- shardId,
965
- clients: /* @__PURE__ */ new Map()
966
- // Track clients connected through this shard
967
- });
968
- }
969
- /**
970
- * @method onMessage
971
- * @async
972
- * @param {string} message - The message received from a user or shard.
973
- * @param {Party.Connection} sender - The connection object of the sender.
974
- * @description Processes incoming messages, handling differently based on if sender is shard or client.
975
- * @returns {Promise<void>}
976
- */
977
- async onMessage(message, sender) {
978
- if (sender.state && sender.state.shard) {
979
- await this.handleShardMessage(message, sender);
980
- return;
981
- }
982
- let json;
983
- try {
984
- json = JSON.parse(message);
985
- } catch (e) {
986
- return;
987
- }
988
- const result = Message.safeParse(json);
989
- if (!result.success) {
990
- return;
991
- }
992
- const subRoom = await this.getSubRoom();
993
- if (!subRoom) {
994
- console.warn("Room not found");
995
- return;
996
- }
997
- const roomGuards = subRoom.constructor["_roomGuards"] || [];
998
- for (const guard of roomGuards) {
999
- const isAuthorized = await guard(sender, result.data.value, this.room);
1000
- if (!isAuthorized) {
1001
- return;
1002
- }
1003
- }
1004
- const actions = subRoom.constructor["_actionMetadata"];
1005
- if (actions) {
1006
- const signal2 = this.getUsersProperty(subRoom);
1007
- const { publicId } = sender.state;
1008
- const user = signal2?.()[publicId];
1009
- const actionName = actions.get(result.data.action);
1010
- if (actionName) {
1011
- const guards = subRoom.constructor["_actionGuards"]?.get(actionName.key) || [];
1012
- for (const guard of guards) {
1013
- const isAuthorized = await guard(sender, result.data.value);
1014
- if (!isAuthorized) {
1015
- return;
1016
- }
1017
- }
1018
- if (actionName.bodyValidation) {
1019
- const bodyResult = actionName.bodyValidation.safeParse(result.data.value);
1020
- if (!bodyResult.success) {
1021
- return;
1022
- }
1023
- }
1024
- await awaitReturn(subRoom[actionName.key](user, result.data.value, sender));
1025
- }
1026
- }
1027
- }
1028
- /**
1029
- * @method handleShardMessage
1030
- * @private
1031
- * @async
1032
- * @param {string} message - The message received from a shard.
1033
- * @param {Party.Connection} shardConnection - The connection object of the shard.
1034
- * @description Processes messages from shards, extracting client information.
1035
- * @returns {Promise<void>}
1036
- */
1037
- async handleShardMessage(message, shardConnection) {
1038
- let parsedMessage;
1039
- try {
1040
- parsedMessage = JSON.parse(message);
1041
- } catch (e) {
1042
- console.error("Error parsing shard message:", e);
1043
- return;
1044
- }
1045
- const shardState = shardConnection.state;
1046
- shardState.clients;
1047
- switch (parsedMessage.type) {
1048
- case "shard.clientConnected":
1049
- await this.handleShardClientConnect(parsedMessage, shardConnection);
1050
- break;
1051
- case "shard.clientMessage":
1052
- await this.handleShardClientMessage(parsedMessage, shardConnection);
1053
- break;
1054
- case "shard.clientDisconnected":
1055
- await this.handleShardClientDisconnect(parsedMessage, shardConnection);
1056
- break;
1057
- default:
1058
- console.warn(`Unknown shard message type: ${parsedMessage.type}`);
1059
- }
1060
- }
1061
- /**
1062
- * @method handleShardClientConnect
1063
- * @private
1064
- * @async
1065
- * @param {Object} message - The client connection message from a shard.
1066
- * @param {Party.Connection} shardConnection - The connection object of the shard.
1067
- * @description Handles a new client connection via a shard.
1068
- * @returns {Promise<void>}
1069
- */
1070
- async handleShardClientConnect(message, shardConnection) {
1071
- const { privateId, requestInfo } = message;
1072
- const shardState = shardConnection.state;
1073
- const virtualContext = {
1074
- request: requestInfo ? {
1075
- headers: new Headers(requestInfo.headers),
1076
- method: requestInfo.method,
1077
- url: requestInfo.url
1078
- } : void 0
1079
- };
1080
- const virtualConnection = {
1081
- id: privateId,
1082
- send: /* @__PURE__ */ __name((data) => {
1083
- shardConnection.send(JSON.stringify({
1084
- targetClientId: privateId,
1085
- data
1086
- }));
1087
- }, "send"),
1088
- state: {},
1089
- setState: /* @__PURE__ */ __name((state) => {
1090
- const clients = shardState.clients;
1091
- const currentState = clients.get(privateId) || {};
1092
- const mergedState = Object.assign({}, currentState, state);
1093
- clients.set(privateId, mergedState);
1094
- virtualConnection.state = clients.get(privateId);
1095
- return virtualConnection.state;
1096
- }, "setState"),
1097
- close: /* @__PURE__ */ __name(() => {
1098
- shardConnection.send(JSON.stringify({
1099
- type: "shard.closeClient",
1100
- privateId
1101
- }));
1102
- if (shardState.clients) {
1103
- shardState.clients.delete(privateId);
1104
- }
1105
- }, "close")
1106
- };
1107
- if (!shardState.clients.has(privateId)) {
1108
- shardState.clients.set(privateId, {});
1109
- }
1110
- await this.onConnectClient(virtualConnection, virtualContext);
1111
- }
1112
- /**
1113
- * @method handleShardClientMessage
1114
- * @private
1115
- * @async
1116
- * @param {Object} message - The client message from a shard.
1117
- * @param {Party.Connection} shardConnection - The connection object of the shard.
1118
- * @description Handles a message from a client via a shard.
1119
- * @returns {Promise<void>}
1120
- */
1121
- async handleShardClientMessage(message, shardConnection) {
1122
- const { privateId, publicId, payload } = message;
1123
- const shardState = shardConnection.state;
1124
- const clients = shardState.clients;
1125
- if (!clients.has(privateId)) {
1126
- console.warn(`Received message from unknown client ${privateId}, creating virtual connection`);
1127
- clients.set(privateId, {
1128
- publicId
1129
- });
1130
- }
1131
- const virtualConnection = {
1132
- id: privateId,
1133
- send: /* @__PURE__ */ __name((data) => {
1134
- shardConnection.send(JSON.stringify({
1135
- targetClientId: privateId,
1136
- data
1137
- }));
1138
- }, "send"),
1139
- state: clients.get(privateId),
1140
- setState: /* @__PURE__ */ __name((state) => {
1141
- const currentState = clients.get(privateId) || {};
1142
- const mergedState = Object.assign({}, currentState, state);
1143
- clients.set(privateId, mergedState);
1144
- virtualConnection.state = clients.get(privateId);
1145
- return virtualConnection.state;
1146
- }, "setState"),
1147
- close: /* @__PURE__ */ __name(() => {
1148
- shardConnection.send(JSON.stringify({
1149
- type: "shard.closeClient",
1150
- privateId
1151
- }));
1152
- if (shardState.clients) {
1153
- shardState.clients.delete(privateId);
1154
- }
1155
- }, "close")
1156
- };
1157
- const payloadString = typeof payload === "string" ? payload : JSON.stringify(payload);
1158
- await this.onMessage(payloadString, virtualConnection);
1159
- }
1160
- /**
1161
- * @method handleShardClientDisconnect
1162
- * @private
1163
- * @async
1164
- * @param {Object} message - The client disconnection message from a shard.
1165
- * @param {Party.Connection} shardConnection - The connection object of the shard.
1166
- * @description Handles a client disconnection via a shard.
1167
- * @returns {Promise<void>}
1168
- */
1169
- async handleShardClientDisconnect(message, shardConnection) {
1170
- const { privateId, publicId } = message;
1171
- const shardState = shardConnection.state;
1172
- const clients = shardState.clients;
1173
- const clientState = clients.get(privateId);
1174
- if (!clientState) {
1175
- console.warn(`Disconnection for unknown client ${privateId}`);
1176
- return;
1177
- }
1178
- const virtualConnection = {
1179
- id: privateId,
1180
- send: /* @__PURE__ */ __name(() => {
1181
- }, "send"),
1182
- state: clientState,
1183
- setState: /* @__PURE__ */ __name(() => {
1184
- return {};
1185
- }, "setState"),
1186
- close: /* @__PURE__ */ __name(() => {
1187
- }, "close")
1188
- };
1189
- await this.onClose(virtualConnection);
1190
- clients.delete(privateId);
1191
- }
1192
- /**
1193
- * @method onClose
1194
- * @async
1195
- * @param {Party.Connection} conn - The connection object of the disconnecting user.
1196
- * @description Handles user disconnection, removing them from the room and triggering the onLeave event..
1197
- * @returns {Promise<void>}
1198
- *
1199
- * @example
1200
- * ```typescript
1201
- * server.onClose = async (conn) => {
1202
- * await server.onClose(conn);
1203
- * console.log("User disconnected:", conn.id);
1204
- * };
1205
- * ```
1206
- */
1207
- async onClose(conn) {
1208
- const subRoom = await this.getSubRoom();
1209
- if (!subRoom) {
1210
- return;
1211
- }
1212
- const signal2 = this.getUsersProperty(subRoom);
1213
- if (!conn.state) {
1214
- return;
1215
- }
1216
- const privateId = conn.id;
1217
- const { publicId } = conn.state;
1218
- const user = signal2?.()[publicId];
1219
- if (!user) return;
1220
- await this.updateSessionConnection(privateId, false);
1221
- const connectionUpdated = this.updateUserConnectionStatus(user, false);
1222
- await awaitReturn(subRoom["onLeave"]?.(user, conn));
1223
- if (!connectionUpdated) {
1224
- this.broadcast({
1225
- type: "user_disconnected",
1226
- value: {
1227
- publicId
1228
- }
1229
- }, subRoom);
1230
- }
1231
- }
1232
- async onAlarm() {
1233
- const subRoom = await this.getSubRoom();
1234
- await awaitReturn(subRoom["onAlarm"]?.(subRoom));
1235
- }
1236
- async onError(connection, error) {
1237
- const subRoom = await this.getSubRoom();
1238
- await awaitReturn(subRoom["onError"]?.(connection, error));
1239
- }
1240
- /**
1241
- * @method onRequest
1242
- * @async
1243
- * @param {Party.Request} req - The HTTP request to handle
1244
- * @description Handles HTTP requests, either directly from clients or forwarded by shards
1245
- * @returns {Promise<Response>} The response to return to the client
1246
- */
1247
- async onRequest(req) {
1248
- const isFromShard = req.headers.has("x-forwarded-by-shard");
1249
- const shardId = req.headers.get("x-shard-id");
1250
- const res = new ServerResponse([
1251
- createCorsInterceptor()
1252
- ]);
1253
- if (req.method === "OPTIONS") {
1254
- return res.status(200).send({});
1255
- }
1256
- if (isFromShard) {
1257
- return this.handleShardRequest(req, res, shardId);
1258
- }
1259
- return this.handleDirectRequest(req, res);
1260
- }
1261
- /**
1262
- * @method handleSessionRestore
1263
- * @private
1264
- * @async
1265
- * @param {Party.Request} req - The HTTP request for session restore
1266
- * @param {ServerResponse} res - The response object
1267
- * @description Handles session restoration from transfer data, creates session from privateId
1268
- * @returns {Promise<Response>} The response to return to the client
1269
- */
1270
- async handleSessionRestore(req, res) {
1271
- try {
1272
- const transferData = await req.json();
1273
- const { privateId, userSnapshot, sessionState, publicId } = transferData;
1274
- if (!privateId || !publicId) {
1275
- return res.badRequest("Missing privateId or publicId in transfer data");
1276
- }
1277
- const subRoom = await this.getSubRoom();
1278
- if (!subRoom) {
1279
- return res.serverError("Room not available");
1280
- }
1281
- await this.saveSession(privateId, {
1282
- publicId,
1283
- state: sessionState,
1284
- created: Date.now(),
1285
- connected: false
1286
- // Will be set to true when user connects
1287
- });
1288
- if (userSnapshot) {
1289
- const signal2 = this.getUsersProperty(subRoom);
1290
- const usersPropName = this.getUsersPropName(subRoom);
1291
- if (signal2 && usersPropName) {
1292
- const { classType } = signal2.options;
1293
- const user = isClass(classType) ? new classType() : classType();
1294
- load(user, userSnapshot, true);
1295
- signal2()[publicId] = user;
1296
- await this.room.storage.put(`${usersPropName}.${publicId}`, userSnapshot);
1297
- }
1298
- }
1299
- const transferToken = generateShortUUID$1();
1300
- await this.room.storage.put(`transfer:${transferToken}`, {
1301
- privateId,
1302
- publicId,
1303
- restored: true
1304
- });
1305
- return res.success({
1306
- transferToken
1307
- });
1308
- } catch (error) {
1309
- console.error("Error restoring session:", error);
1310
- return res.serverError("Failed to restore session");
1311
- }
1312
- }
1313
- /**
1314
- * @method handleDirectRequest
1315
- * @private
1316
- * @async
1317
- * @param {Party.Request} req - The HTTP request received directly from a client
1318
- * @description Processes requests received directly from clients
1319
- * @returns {Promise<Response>} The response to return to the client
1320
- */
1321
- async handleDirectRequest(req, res) {
1322
- const subRoom = await this.getSubRoom();
1323
- if (!subRoom) {
1324
- return res.notFound();
1325
- }
1326
- const url = new URL(req.url);
1327
- if (url.pathname.endsWith("/session-transfer") && req.method === "POST") {
1328
- return this.handleSessionRestore(req, res);
1329
- }
1330
- const response2 = await this.tryMatchRequestHandler(req, res, subRoom);
1331
- if (response2) {
1332
- return response2;
1333
- }
1334
- const legacyResponse = await awaitReturn(subRoom["onRequest"]?.(req, res));
1335
- if (!legacyResponse) {
1336
- return res.notFound();
1337
- }
1338
- if (legacyResponse instanceof Response) {
1339
- return legacyResponse;
1340
- }
1341
- return res.success(legacyResponse);
1342
- }
1343
- /**
1344
- * @method tryMatchRequestHandler
1345
- * @private
1346
- * @async
1347
- * @param {Party.Request} req - The HTTP request to handle
1348
- * @param {Object} subRoom - The room instance
1349
- * @description Attempts to match the request to a registered @Request handler
1350
- * @returns {Promise<Response | null>} The response or null if no handler matched
1351
- */
1352
- async tryMatchRequestHandler(req, res, subRoom) {
1353
- const requestHandlers = subRoom.constructor["_requestMetadata"];
1354
- if (!requestHandlers) {
1355
- return null;
1356
- }
1357
- const url = new URL(req.url);
1358
- const method = req.method;
1359
- let pathname = url.pathname;
1360
- pathname = "/" + pathname.split("/").slice(4).join("/");
1361
- for (const [routeKey, handler] of requestHandlers.entries()) {
1362
- const firstColonIndex = routeKey.indexOf(":");
1363
- const handlerMethod = routeKey.substring(0, firstColonIndex);
1364
- const handlerPath = routeKey.substring(firstColonIndex + 1);
1365
- if (handlerMethod !== method) {
1366
- continue;
1367
- }
1368
- if (this.pathMatches(pathname, handlerPath)) {
1369
- const params = this.extractPathParams(pathname, handlerPath);
1370
- const guards = subRoom.constructor["_actionGuards"]?.get(handler.key) || [];
1371
- for (const guard of guards) {
1372
- const isAuthorized = await guard(null, req, this.room);
1373
- if (isAuthorized instanceof Response) {
1374
- return isAuthorized;
1375
- }
1376
- if (!isAuthorized) {
1377
- return res.notPermitted();
1378
- }
1379
- }
1380
- let bodyData = null;
1381
- if (handler.bodyValidation && [
1382
- "POST",
1383
- "PUT",
1384
- "PATCH"
1385
- ].includes(method)) {
1386
- try {
1387
- const contentType = req.headers.get("content-type") || "";
1388
- if (contentType.includes("application/json")) {
1389
- const body = await req.json();
1390
- const validation = handler.bodyValidation.safeParse(body);
1391
- if (!validation.success) {
1392
- return res.badRequest("Invalid request body", {
1393
- details: validation.error
1394
- });
1395
- }
1396
- bodyData = validation.data;
1397
- }
1398
- } catch (error) {
1399
- return res.badRequest("Failed to parse request body");
1400
- }
1401
- }
1402
- try {
1403
- req["data"] = bodyData;
1404
- req["params"] = params;
1405
- const result = await awaitReturn(subRoom[handler.key](req, res));
1406
- if (result instanceof Response) {
1407
- return result;
1408
- }
1409
- return res.success(result);
1410
- } catch (error) {
1411
- console.error("Error executing request handler:", error);
1412
- return res.serverError();
1413
- }
1414
- }
1415
- }
1416
- return null;
1417
- }
1418
- /**
1419
- * @method pathMatches
1420
- * @private
1421
- * @param {string} requestPath - The path from the request
1422
- * @param {string} handlerPath - The path pattern from the handler
1423
- * @description Checks if a request path matches a handler path pattern
1424
- * @returns {boolean} True if the paths match
1425
- */
1426
- pathMatches(requestPath, handlerPath) {
1427
- const pathRegexString = handlerPath.replace(/\//g, "\\/").replace(/:([^\/]+)/g, "([^/]+)");
1428
- const pathRegex = new RegExp(`^${pathRegexString}`);
1429
- return pathRegex.test(requestPath);
1430
- }
1431
- /**
1432
- * @method extractPathParams
1433
- * @private
1434
- * @param {string} requestPath - The path from the request
1435
- * @param {string} handlerPath - The path pattern from the handler
1436
- * @description Extracts path parameters from the request path based on the handler pattern
1437
- * @returns {Object} An object containing the path parameters
1438
- */
1439
- extractPathParams(requestPath, handlerPath) {
1440
- const params = {};
1441
- const paramNames = [];
1442
- handlerPath.split("/").forEach((segment) => {
1443
- if (segment.startsWith(":")) {
1444
- paramNames.push(segment.substring(1));
1445
- }
1446
- });
1447
- const pathRegexString = handlerPath.replace(/\//g, "\\/").replace(/:([^\/]+)/g, "([^/]+)");
1448
- const pathRegex = new RegExp(`^${pathRegexString}`);
1449
- const matches = requestPath.match(pathRegex);
1450
- if (matches && matches.length > 1) {
1451
- for (let i = 0; i < paramNames.length; i++) {
1452
- params[paramNames[i]] = matches[i + 1];
1453
- }
1454
- }
1455
- return params;
1456
- }
1457
- /**
1458
- * @method handleShardRequest
1459
- * @private
1460
- * @async
1461
- * @param {Party.Request} req - The HTTP request forwarded by a shard
1462
- * @param {string | null} shardId - The ID of the shard that forwarded the request
1463
- * @description Processes requests forwarded by shards, preserving client context
1464
- * @returns {Promise<Response>} The response to return to the shard (which will forward it to the client)
1465
- */
1466
- async handleShardRequest(req, res, shardId) {
1467
- const subRoom = await this.getSubRoom();
1468
- if (!subRoom) {
1469
- return res.notFound();
1470
- }
1471
- const originalClientIp = req.headers.get("x-original-client-ip");
1472
- const enhancedReq = this.createEnhancedRequest(req, originalClientIp);
1473
- try {
1474
- const response2 = await this.tryMatchRequestHandler(enhancedReq, res, subRoom);
1475
- if (response2) {
1476
- return response2;
1477
- }
1478
- const legacyResponse = await awaitReturn(subRoom["onRequest"]?.(enhancedReq, res));
1479
- if (!legacyResponse) {
1480
- return res.notFound();
1481
- }
1482
- if (legacyResponse instanceof Response) {
1483
- return legacyResponse;
1484
- }
1485
- return res.success(legacyResponse);
1486
- } catch (error) {
1487
- console.error(`Error processing request from shard ${shardId}:`, error);
1488
- return res.serverError();
1489
- }
1490
- }
1491
- /**
1492
- * @method createEnhancedRequest
1493
- * @private
1494
- * @param {Party.Request} originalReq - The original request received from the shard
1495
- * @param {string | null} originalClientIp - The original client IP, if available
1496
- * @description Creates an enhanced request object that preserves the original client context
1497
- * @returns {Party.Request} The enhanced request object
1498
- */
1499
- createEnhancedRequest(originalReq, originalClientIp) {
1500
- const clonedReq = originalReq.clone();
1501
- clonedReq.viaShard = true;
1502
- if (originalClientIp) {
1503
- clonedReq.originalClientIp = originalClientIp;
1504
- }
1505
- return clonedReq;
1506
- }
1507
- };
1508
-
1509
- // src/shard.ts
1510
- var Shard = class {
1511
- static {
1512
- __name(this, "Shard");
1513
- }
1514
- room;
1515
- ws;
1516
- connectionMap;
1517
- mainServerStub;
1518
- worldUrl;
1519
- worldId;
1520
- lastReportedConnections;
1521
- statsInterval;
1522
- statsIntervalId;
1523
- constructor(room) {
1524
- this.room = room;
1525
- this.connectionMap = /* @__PURE__ */ new Map();
1526
- this.worldUrl = null;
1527
- this.worldId = "default";
1528
- this.lastReportedConnections = 0;
1529
- this.statsInterval = 3e4;
1530
- this.statsIntervalId = null;
1531
- }
1532
- async onStart() {
1533
- const roomId = this.room.id.split(":")[0];
1534
- const roomStub = this.room.context.parties.main.get(roomId);
1535
- if (!roomStub) {
1536
- console.warn("No room room stub found in main party context");
1537
- return;
1538
- }
1539
- this.mainServerStub = roomStub;
1540
- this.ws = await roomStub.socket({
1541
- headers: {
1542
- "x-shard-id": this.room.id
1543
- }
1544
- });
1545
- this.ws.addEventListener("message", (event) => {
1546
- try {
1547
- const message = JSON.parse(event.data);
1548
- if (message.targetClientId) {
1549
- const clientConn = this.connectionMap.get(message.targetClientId);
1550
- if (clientConn) {
1551
- delete message.targetClientId;
1552
- clientConn.send(message.data);
1553
- }
1554
- } else {
1555
- this.room.broadcast(event.data);
1556
- }
1557
- } catch (error) {
1558
- console.error("Error processing message from main server:", error);
1559
- }
1560
- });
1561
- await this.updateWorldStats();
1562
- this.startPeriodicStatsUpdates();
1563
- }
1564
- startPeriodicStatsUpdates() {
1565
- if (!this.worldUrl) {
1566
- return;
1567
- }
1568
- if (this.statsIntervalId) {
1569
- clearInterval(this.statsIntervalId);
1570
- }
1571
- this.statsIntervalId = setInterval(() => {
1572
- this.updateWorldStats().catch((error) => {
1573
- console.error("Error in periodic stats update:", error);
1574
- });
1575
- }, this.statsInterval);
1576
- }
1577
- stopPeriodicStatsUpdates() {
1578
- if (this.statsIntervalId) {
1579
- clearInterval(this.statsIntervalId);
1580
- this.statsIntervalId = null;
1581
- }
1582
- }
1583
- onConnect(conn, ctx) {
1584
- this.connectionMap.set(conn.id, conn);
1585
- const headers = {};
1586
- if (ctx.request?.headers) {
1587
- ctx.request.headers.forEach((value, key) => {
1588
- headers[key] = value;
1589
- });
1590
- }
1591
- const requestInfo = ctx.request ? {
1592
- headers,
1593
- url: ctx.request.url,
1594
- method: ctx.request.method
1595
- } : null;
1596
- this.ws.send(JSON.stringify({
1597
- type: "shard.clientConnected",
1598
- privateId: conn.id,
1599
- requestInfo
1600
- }));
1601
- this.updateWorldStats();
1602
- }
1603
- onMessage(message, sender) {
1604
- try {
1605
- const parsedMessage = typeof message === "string" ? JSON.parse(message) : message;
1606
- const wrappedMessage = JSON.stringify({
1607
- type: "shard.clientMessage",
1608
- privateId: sender.id,
1609
- publicId: sender.state?.publicId,
1610
- payload: parsedMessage
1611
- });
1612
- this.ws.send(wrappedMessage);
1613
- } catch (error) {
1614
- console.error("Error forwarding message to main server:", error);
1615
- }
1616
- }
1617
- onClose(conn) {
1618
- this.connectionMap.delete(conn.id);
1619
- this.ws.send(JSON.stringify({
1620
- type: "shard.clientDisconnected",
1621
- privateId: conn.id,
1622
- publicId: conn.state?.publicId
1623
- }));
1624
- this.updateWorldStats();
1625
- }
1626
- async updateWorldStats() {
1627
- const currentConnections = this.connectionMap.size;
1628
- if (currentConnections === this.lastReportedConnections) {
1629
- return true;
1630
- }
1631
- try {
1632
- const worldRoom = this.room.context.parties.world.get("world-default");
1633
- const response2 = await worldRoom.fetch("/update-shard", {
1634
- method: "POST",
1635
- headers: {
1636
- "Content-Type": "application/json",
1637
- "x-access-shard": this.room.env.SHARD_SECRET
1638
- },
1639
- body: JSON.stringify({
1640
- shardId: this.room.id,
1641
- connections: currentConnections
1642
- })
1643
- });
1644
- if (!response2.ok) {
1645
- const errorData = await response2.json().catch(() => ({
1646
- error: "Unknown error"
1647
- }));
1648
- console.error(`Failed to update World stats: ${response2.status} - ${errorData.error || "Unknown error"}`);
1649
- return false;
1650
- }
1651
- this.lastReportedConnections = currentConnections;
1652
- return true;
1653
- } catch (error) {
1654
- console.error("Error updating World stats:", error);
1655
- return false;
1656
- }
1657
- }
1658
- /**
1659
- * @method onRequest
1660
- * @async
1661
- * @param {Party.Request} req - The HTTP request to handle
1662
- * @description Forwards HTTP requests to the main server, preserving client context
1663
- * @returns {Promise<Response>} The response from the main server
1664
- */
1665
- async onRequest(req) {
1666
- if (!this.mainServerStub) {
1667
- return response(503, {
1668
- error: "Shard not connected to main server"
1669
- });
1670
- }
1671
- try {
1672
- const url = new URL(req.url);
1673
- const path = url.pathname;
1674
- const method = req.method;
1675
- let body = null;
1676
- if (method !== "GET" && method !== "HEAD") {
1677
- body = await req.text();
1678
- }
1679
- const headers = new Headers();
1680
- req.headers.forEach((value, key) => {
1681
- headers.set(key, value);
1682
- });
1683
- headers.set("x-shard-id", this.room.id);
1684
- headers.set("x-forwarded-by-shard", "true");
1685
- const clientIp = req.headers.get("x-forwarded-for") || "unknown";
1686
- if (clientIp) {
1687
- headers.set("x-original-client-ip", clientIp);
1688
- }
1689
- const requestInit = {
1690
- method,
1691
- headers,
1692
- body
1693
- };
1694
- const response2 = await this.mainServerStub.fetch(path, requestInit);
1695
- return response2;
1696
- } catch (error) {
1697
- return response(500, {
1698
- error: "Error forwarding request"
1699
- });
1700
- }
1701
- }
1702
- /**
1703
- * @method onAlarm
1704
- * @async
1705
- * @description Executed periodically, used to perform maintenance tasks
1706
- */
1707
- async onAlarm() {
1708
- await this.updateWorldStats();
1709
- }
1710
- };
1711
-
1712
- // src/testing.ts
1713
- async function testRoom(Room3, options = {}) {
1714
- const createServer = /* @__PURE__ */ __name((io2) => {
1715
- const server2 = new Server(io2);
1716
- server2.rooms = [
1717
- Room3
1718
- ];
1719
- return server2;
1720
- }, "createServer");
1721
- const isShard = options.shard || false;
1722
- const io = new ServerIo(Room3.path, isShard ? {
1723
- parties: {
1724
- game: createServer,
1725
- ...options.parties || {}
1726
- },
1727
- partyFn: options.partyFn,
1728
- env: options.env
1729
- } : {
1730
- parties: options.parties,
1731
- partyFn: options.partyFn,
1732
- env: options.env
1733
- });
1734
- Room3.prototype.throttleSync = 0;
1735
- Room3.prototype.throttleStorage = 0;
1736
- Room3.prototype.options = options;
1737
- let server;
1738
- if (options.shard) {
1739
- const shardServer = new Shard(io);
1740
- shardServer.subRoom = null;
1741
- server = shardServer;
1742
- if (io.context.parties.main instanceof Map) {
1743
- for (const lobby of io.context.parties.main.values()) {
1744
- await lobby.server.onStart();
1745
- }
1746
- }
1747
- } else {
1748
- server = await createServer(io);
1749
- if (io.context.parties.main instanceof Map) {
1750
- for (const lobby of io.context.parties.main.values()) {
1751
- if (lobby.server && lobby.server !== server) {
1752
- await lobby.server.onStart();
1753
- }
1754
- }
1755
- }
1756
- }
1757
- await server.onStart();
1758
- return {
1759
- server,
1760
- room: server.subRoom,
1761
- createClient: /* @__PURE__ */ __name(async (id2, opts) => {
1762
- const client = await io.connection(server, id2, opts);
1763
- return client;
1764
- }, "createClient"),
1765
- getServerUser: /* @__PURE__ */ __name(async (client, prop = "users") => {
1766
- const privateId = client.conn.id;
1767
- const session = await server.getSession(privateId);
1768
- return server.subRoom[prop]()[session?.publicId];
1769
- }, "getServerUser")
1770
- };
1771
- }
1772
- __name(testRoom, "testRoom");
1773
- async function request(room, path, options = {
1774
- method: "GET"
1775
- }) {
1776
- const url = new URL("http://localhost" + path);
1777
- const request1 = new Request(url.toString(), options);
1778
- const response2 = await room.onRequest(request1);
1779
- return response2;
1780
- }
1781
- __name(request, "request");
1782
- function tick(ms = 0) {
1783
- return new Promise((resolve) => setTimeout(resolve, ms));
1784
- }
1785
- __name(tick, "tick");
1786
-
1787
- // src/mock.ts
1788
- var MockPartyClient = class {
1789
- static {
1790
- __name(this, "MockPartyClient");
1791
- }
1792
- server;
1793
- events;
1794
- id;
1795
- conn;
1796
- constructor(server, id2) {
1797
- this.server = server;
1798
- this.events = /* @__PURE__ */ new Map();
1799
- this.id = id2 || generateShortUUID();
1800
- this.conn = new MockConnection(this);
1801
- }
1802
- addEventListener(event, cb) {
1803
- if (!this.events.has(event)) {
1804
- this.events.set(event, []);
1805
- }
1806
- this.events.get(event).push(cb);
1807
- }
1808
- removeEventListener(event, cb) {
1809
- if (!this.events.has(event)) return;
1810
- const callbacks = this.events.get(event);
1811
- const index = callbacks.indexOf(cb);
1812
- if (index !== -1) {
1813
- callbacks.splice(index, 1);
1814
- }
1815
- if (callbacks.length === 0) {
1816
- this.events.delete(event);
1817
- }
1818
- }
1819
- _trigger(event, data) {
1820
- const callbacks = this.events.get(event);
1821
- if (callbacks) {
1822
- for (const cb of callbacks) {
1823
- cb(data);
1824
- }
1825
- }
1826
- }
1827
- send(data) {
1828
- return this.server.onMessage(JSON.stringify(data), this.conn);
1829
- }
1830
- };
1831
- var MockLobby = class MockLobby2 {
1832
- static {
1833
- __name(this, "MockLobby");
1834
- }
1835
- server;
1836
- lobbyId;
1837
- constructor(server, lobbyId) {
1838
- this.server = server;
1839
- this.lobbyId = lobbyId;
1840
- }
1841
- socket(_init) {
1842
- return new MockPartyClient(this.server);
1843
- }
1844
- async connection(idOrOptions, maybeOptions) {
1845
- const id2 = typeof idOrOptions === "string" ? idOrOptions : idOrOptions?.id;
1846
- const options = (typeof idOrOptions === "string" ? maybeOptions : idOrOptions) || {};
1847
- return this.server.room.connection(this.server, id2, options);
1848
- }
1849
- fetch(url, options) {
1850
- const baseUrl = url.includes("shard") ? "" : "/parties/main/" + this.lobbyId;
1851
- return request(this.server, baseUrl + url, options);
1852
- }
1853
- };
1854
- var MockContext = class MockContext2 {
1855
- static {
1856
- __name(this, "MockContext");
1857
- }
1858
- room;
1859
- parties;
1860
- constructor(room, options = {}) {
1861
- this.room = room;
1862
- this.parties = {
1863
- main: /* @__PURE__ */ new Map()
1864
- };
1865
- const parties = options.parties || {};
1866
- if (options.partyFn) {
1867
- const serverCache = /* @__PURE__ */ new Map();
1868
- this.parties.main = {
1869
- get: /* @__PURE__ */ __name(async (lobbyId) => {
1870
- if (!serverCache.has(lobbyId)) {
1871
- const server = await options.partyFn(lobbyId);
1872
- serverCache.set(lobbyId, new MockLobby(server, lobbyId));
1873
- }
1874
- return serverCache.get(lobbyId);
1875
- }, "get")
1876
- };
1877
- } else {
1878
- for (let lobbyId in parties) {
1879
- const server = parties[lobbyId](room);
1880
- this.parties.main.set(lobbyId, new MockLobby(server, lobbyId));
1881
- }
1882
- }
1883
- }
1884
- };
1885
- var MockPartyRoom = class MockPartyRoom2 {
1886
- static {
1887
- __name(this, "MockPartyRoom");
1888
- }
1889
- id;
1890
- clients;
1891
- storage;
1892
- context;
1893
- env;
1894
- constructor(id2, options = {}) {
1895
- this.id = id2;
1896
- this.clients = /* @__PURE__ */ new Map();
1897
- this.storage = new Storage();
1898
- this.env = {};
1899
- this.id = id2 || generateShortUUID();
1900
- this.context = new MockContext(this, {
1901
- parties: options.parties,
1902
- partyFn: options.partyFn
1903
- });
1904
- this.env = options.env || {};
1905
- }
1906
- async connection(server, id2, opts) {
1907
- const socket = new MockPartyClient(server, id2);
1908
- const url = new URL("http://localhost");
1909
- if (opts?.query) {
1910
- for (const [key, value] of Object.entries(opts.query)) {
1911
- url.searchParams.set(key, String(value));
1912
- }
1913
- }
1914
- const request2 = new Request(url.toString(), {
1915
- method: "GET",
1916
- headers: {
1917
- "Content-Type": "application/json",
1918
- ...opts?.headers || {}
1919
- }
1920
- });
1921
- await server.onConnect(socket.conn, {
1922
- request: request2
1923
- });
1924
- this.clients.set(socket.id, socket);
1925
- return socket;
1926
- }
1927
- broadcast(data) {
1928
- this.clients.forEach((client) => {
1929
- client._trigger("message", data);
1930
- });
1931
- }
1932
- getConnection(id2) {
1933
- return this.clients.get(id2);
1934
- }
1935
- getConnections() {
1936
- return Array.from(this.clients.values()).map((client) => client.conn);
1937
- }
1938
- clear() {
1939
- this.clients.clear();
1940
- }
1941
- };
1942
- var MockConnection = class {
1943
- static {
1944
- __name(this, "MockConnection");
1945
- }
1946
- client;
1947
- server;
1948
- id;
1949
- constructor(client) {
1950
- this.client = client;
1951
- this.state = {};
1952
- this.server = client.server;
1953
- this.id = client.id;
1954
- }
1955
- state;
1956
- setState(value) {
1957
- this.state = value;
1958
- }
1959
- send(data) {
1960
- this.client._trigger("message", data);
1961
- }
1962
- close() {
1963
- this.server.onClose(this);
1964
- }
1965
- };
1966
- var ServerIo = MockPartyRoom;
1967
- var ClientIo = MockPartyClient;
1968
-
1969
- // src/jwt.ts
1970
- var JWTAuth = class {
1971
- static {
1972
- __name(this, "JWTAuth");
1973
- }
1974
- secret;
1975
- encoder;
1976
- decoder;
1977
- /**
1978
- * Constructor for the JWTAuth class
1979
- * @param {string} secret - The secret key used for signing and verifying tokens
1980
- */
1981
- constructor(secret) {
1982
- if (!secret || typeof secret !== "string") {
1983
- throw new Error("Secret is required and must be a string");
27
+ var worldWidth = computed(function () { var _a; return (_a = sceneData()) === null || _a === void 0 ? void 0 : _a.width; });
28
+ var worldHeight = computed(function () { var _a; return (_a = sceneData()) === null || _a === void 0 ? void 0 : _a.height; });
29
+ effect(function () {
30
+ if (sceneData() && !sceneData().component) {
31
+ throw new Error("Warning ! You need to define a component for the scene. Use provideLoadMap() to define a component for the scene.");
1984
32
  }
1985
- this.secret = secret;
1986
- this.encoder = new TextEncoder();
1987
- this.decoder = new TextDecoder();
1988
- }
1989
- /**
1990
- * Convert the secret to a CryptoKey for HMAC operations
1991
- * @returns {Promise<CryptoKey>} - The CryptoKey for HMAC operations
1992
- */
1993
- async getSecretKey() {
1994
- const keyData = this.encoder.encode(this.secret);
1995
- return await crypto.subtle.importKey(
1996
- "raw",
1997
- keyData,
1998
- {
1999
- name: "HMAC",
2000
- hash: {
2001
- name: "SHA-256"
2002
- }
2003
- },
2004
- false,
2005
- [
2006
- "sign",
2007
- "verify"
2008
- ]
2009
- // key usages
2010
- );
2011
- }
2012
- /**
2013
- * Base64Url encode a buffer
2014
- * @param {ArrayBuffer} buffer - The buffer to encode
2015
- * @returns {string} - The base64url encoded string
2016
- */
2017
- base64UrlEncode(buffer) {
2018
- const base64 = btoa(String.fromCharCode(...new Uint8Array(buffer)));
2019
- return base64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
2020
- }
2021
- /**
2022
- * Base64Url decode a string
2023
- * @param {string} base64Url - The base64url encoded string
2024
- * @returns {ArrayBuffer} - The decoded buffer
2025
- */
2026
- base64UrlDecode(base64Url) {
2027
- const padding = "=".repeat((4 - base64Url.length % 4) % 4);
2028
- const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/") + padding;
2029
- const rawData = atob(base64);
2030
- const buffer = new Uint8Array(rawData.length);
2031
- for (let i = 0; i < rawData.length; i++) {
2032
- buffer[i] = rawData.charCodeAt(i);
2033
- }
2034
- return buffer.buffer;
2035
- }
2036
- /**
2037
- * Sign a payload and create a JWT token
2038
- * @param {JWTPayload} payload - The payload to include in the token
2039
- * @param {JWTOptions} [options={}] - Options for the token
2040
- * @param {string | number} [options.expiresIn='1h'] - Token expiration time
2041
- * @returns {Promise<string>} - The JWT token
2042
- */
2043
- async sign(payload, options = {}) {
2044
- if (!payload || typeof payload !== "object") {
2045
- throw new Error("Payload must be an object");
2046
- }
2047
- const expiresIn = options.expiresIn || "1h";
2048
- let exp;
2049
- if (typeof expiresIn === "number") {
2050
- exp = Math.floor(Date.now() / 1e3) + expiresIn;
2051
- } else if (typeof expiresIn === "string") {
2052
- const match = expiresIn.match(/^(\d+)([smhd])$/);
2053
- if (match) {
2054
- const value = parseInt(match[1]);
2055
- const unit = match[2];
2056
- const seconds = {
2057
- "s": value,
2058
- "m": value * 60,
2059
- "h": value * 60 * 60,
2060
- "d": value * 60 * 60 * 24
2061
- }[unit];
2062
- exp = Math.floor(Date.now() / 1e3) + seconds;
2063
- } else {
2064
- throw new Error('Invalid expiresIn format. Use a number (seconds) or a string like "1h", "30m", etc.');
2065
- }
2066
- }
2067
- const fullPayload = {
2068
- ...payload,
2069
- iat: Math.floor(Date.now() / 1e3),
2070
- exp
2071
- };
2072
- const header = {
2073
- alg: "HS256",
2074
- typ: "JWT"
2075
- };
2076
- const encodedHeader = this.base64UrlEncode(this.encoder.encode(JSON.stringify(header)));
2077
- const encodedPayload = this.base64UrlEncode(this.encoder.encode(JSON.stringify(fullPayload)));
2078
- const signatureBase = `${encodedHeader}.${encodedPayload}`;
2079
- const key = await this.getSecretKey();
2080
- const signature = await crypto.subtle.sign({
2081
- name: "HMAC"
2082
- }, key, this.encoder.encode(signatureBase));
2083
- const encodedSignature = this.base64UrlEncode(signature);
2084
- return `${signatureBase}.${encodedSignature}`;
2085
- }
2086
- /**
2087
- * Verify a JWT token and return the decoded payload
2088
- * @param {string} token - The JWT token to verify
2089
- * @returns {Promise<JWTPayload>} - The decoded payload if verification succeeds
2090
- * @throws {Error} - If verification fails
2091
- */
2092
- async verify(token) {
2093
- if (!token || typeof token !== "string") {
2094
- throw new Error("Token is required and must be a string");
2095
- }
2096
- const parts = token.split(".");
2097
- if (parts.length !== 3) {
2098
- throw new Error("Invalid token format");
2099
- }
2100
- const [encodedHeader, encodedPayload, encodedSignature] = parts;
2101
- try {
2102
- const header = JSON.parse(this.decoder.decode(this.base64UrlDecode(encodedHeader)));
2103
- const payload = JSON.parse(this.decoder.decode(this.base64UrlDecode(encodedPayload)));
2104
- if (header.alg !== "HS256") {
2105
- throw new Error(`Unsupported algorithm: ${header.alg}`);
2106
- }
2107
- const now = Math.floor(Date.now() / 1e3);
2108
- if (payload.exp && payload.exp < now) {
2109
- throw new Error("Token has expired");
2110
- }
2111
- const key = await this.getSecretKey();
2112
- const signatureBase = `${encodedHeader}.${encodedPayload}`;
2113
- const signature = this.base64UrlDecode(encodedSignature);
2114
- const isValid = await crypto.subtle.verify({
2115
- name: "HMAC"
2116
- }, key, signature, this.encoder.encode(signatureBase));
2117
- if (!isValid) {
2118
- throw new Error("Invalid signature");
2119
- }
2120
- return payload;
2121
- } catch (error) {
2122
- if (error instanceof Error) {
2123
- throw new Error(`Token verification failed: ${error.message}`);
2124
- }
2125
- throw new Error("Token verification failed: Unknown error");
2126
- }
2127
- }
2128
- };
2129
-
2130
- // src/world.guard.ts
2131
- var guardManageWorld = /* @__PURE__ */ __name(async (_, req, room) => {
2132
- const tokenShard = req.headers.get("x-access-shard");
2133
- if (tokenShard) {
2134
- if (tokenShard !== room.env.SHARD_SECRET) {
2135
- return false;
2136
- }
2137
- return true;
2138
- }
2139
- const url = new URL(req.url);
2140
- const token = req.headers.get("Authorization") ?? url.searchParams.get("world-auth-token");
2141
- if (!token) {
2142
- return false;
2143
- }
2144
- const jwt = new JWTAuth(room.env.AUTH_JWT_SECRET);
2145
- try {
2146
- const payload = await jwt.verify(token);
2147
- if (!payload) {
2148
- return false;
2149
- }
2150
- } catch (error) {
2151
- return false;
2152
- }
2153
- return true;
2154
- }, "guardManageWorld");
2155
-
2156
- // src/world.ts
2157
- function _ts_decorate(decorators, target, key, desc) {
2158
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2159
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2160
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2161
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2162
- }
2163
- __name(_ts_decorate, "_ts_decorate");
2164
- function _ts_metadata(k, v) {
2165
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2166
- }
2167
- __name(_ts_metadata, "_ts_metadata");
2168
- var MAX_PLAYERS_PER_SHARD = 75;
2169
- objectType({
2170
- name: stringType(),
2171
- balancingStrategy: enumType([
2172
- "round-robin",
2173
- "least-connections",
2174
- "random"
2175
- ]),
2176
- public: booleanType(),
2177
- maxPlayersPerShard: numberType().int().positive(),
2178
- minShards: numberType().int().min(0),
2179
- maxShards: numberType().int().positive().optional()
2180
- });
2181
- objectType({
2182
- shardId: stringType(),
2183
- roomId: stringType(),
2184
- url: stringType().url(),
2185
- maxConnections: numberType().int().positive()
2186
- });
2187
- objectType({
2188
- connections: numberType().int().min(0),
2189
- status: enumType([
2190
- "active",
2191
- "maintenance",
2192
- "draining"
2193
- ]).optional()
2194
33
  });
2195
- objectType({
2196
- roomId: stringType(),
2197
- targetShardCount: numberType().int().positive(),
2198
- shardTemplate: objectType({
2199
- urlTemplate: stringType(),
2200
- maxConnections: numberType().int().positive()
2201
- }).optional()
2202
- });
2203
- var RoomConfig = class RoomConfig2 {
2204
- static {
2205
- __name(this, "RoomConfig");
2206
- }
2207
- id;
2208
- name = signal("");
2209
- balancingStrategy = signal("round-robin");
2210
- public = signal(true);
2211
- maxPlayersPerShard = signal(MAX_PLAYERS_PER_SHARD);
2212
- minShards = signal(1);
2213
- maxShards = signal(void 0);
34
+ var onGuiFinish = function (gui, data) {
35
+ guiService.guiClose(gui.name, data);
2214
36
  };
2215
- _ts_decorate([
2216
- id(),
2217
- _ts_metadata("design:type", String)
2218
- ], RoomConfig.prototype, "id", void 0);
2219
- _ts_decorate([
2220
- sync()
2221
- ], RoomConfig.prototype, "name", void 0);
2222
- _ts_decorate([
2223
- sync()
2224
- ], RoomConfig.prototype, "balancingStrategy", void 0);
2225
- _ts_decorate([
2226
- sync()
2227
- ], RoomConfig.prototype, "public", void 0);
2228
- _ts_decorate([
2229
- sync()
2230
- ], RoomConfig.prototype, "maxPlayersPerShard", void 0);
2231
- _ts_decorate([
2232
- sync()
2233
- ], RoomConfig.prototype, "minShards", void 0);
2234
- _ts_decorate([
2235
- sync()
2236
- ], RoomConfig.prototype, "maxShards", void 0);
2237
- var ShardInfo = class ShardInfo2 {
2238
- static {
2239
- __name(this, "ShardInfo");
2240
- }
2241
- id;
2242
- roomId = signal("");
2243
- url = signal("");
2244
- currentConnections = signal(0);
2245
- maxConnections = signal(MAX_PLAYERS_PER_SHARD);
2246
- status = signal("active");
2247
- lastHeartbeat = signal(0);
37
+ var onGuiInteraction = function (gui, name, data) {
38
+ guiService.guiInteraction(gui.name, name, data);
2248
39
  };
2249
- _ts_decorate([
2250
- id(),
2251
- _ts_metadata("design:type", String)
2252
- ], ShardInfo.prototype, "id", void 0);
2253
- _ts_decorate([
2254
- sync()
2255
- ], ShardInfo.prototype, "roomId", void 0);
2256
- _ts_decorate([
2257
- sync()
2258
- ], ShardInfo.prototype, "url", void 0);
2259
- _ts_decorate([
2260
- sync({
2261
- persist: false
2262
- })
2263
- ], ShardInfo.prototype, "currentConnections", void 0);
2264
- _ts_decorate([
2265
- sync()
2266
- ], ShardInfo.prototype, "maxConnections", void 0);
2267
- _ts_decorate([
2268
- sync()
2269
- ], ShardInfo.prototype, "status", void 0);
2270
- _ts_decorate([
2271
- sync()
2272
- ], ShardInfo.prototype, "lastHeartbeat", void 0);
2273
- var WorldRoom = class {
2274
- static {
2275
- __name(this, "WorldRoom");
2276
- }
2277
- room;
2278
- // Synchronized state
2279
- rooms;
2280
- shards;
2281
- // Only persisted state (not synced to clients)
2282
- rrCounters;
2283
- // Configuration
2284
- defaultShardUrlTemplate;
2285
- defaultMaxConnectionsPerShard;
2286
- constructor(room) {
2287
- this.room = room;
2288
- this.rooms = signal({});
2289
- this.shards = signal({});
2290
- this.rrCounters = signal({});
2291
- this.defaultShardUrlTemplate = signal("{shardId}");
2292
- this.defaultMaxConnectionsPerShard = signal(MAX_PLAYERS_PER_SHARD);
2293
- const { AUTH_JWT_SECRET, SHARD_SECRET } = this.room.env;
2294
- if (!AUTH_JWT_SECRET) {
2295
- throw new Error("AUTH_JWT_SECRET env variable is not set");
2296
- }
2297
- if (!SHARD_SECRET) {
2298
- throw new Error("SHARD_SECRET env variable is not set");
2299
- }
2300
- }
2301
- async onJoin(user, conn, ctx) {
2302
- const canConnect = await guardManageWorld(user, ctx.request, this.room);
2303
- conn.setState({
2304
- ...conn.state,
2305
- isAdmin: canConnect
2306
- });
2307
- }
2308
- interceptorPacket(_, obj, conn) {
2309
- if (!conn.state["isAdmin"]) {
2310
- return null;
2311
- }
2312
- return obj;
2313
- }
2314
- // Helper methods
2315
- cleanupInactiveShards() {
2316
- const now = Date.now();
2317
- const timeout = 5 * 60 * 1e3;
2318
- const shardsValue = this.shards();
2319
- Object.values(shardsValue).forEach((shard) => {
2320
- if (now - shard.lastHeartbeat() > timeout) {
2321
- delete this.shards()[shard.id];
2322
- }
2323
- });
2324
- setTimeout(() => this.cleanupInactiveShards(), 6e4);
2325
- }
2326
- // Actions
2327
- async registerRoom(req) {
2328
- const roomConfig = await req.json();
2329
- const roomId = roomConfig.name;
2330
- if (!this.rooms()[roomId]) {
2331
- const newRoom = new RoomConfig();
2332
- newRoom.id = roomId;
2333
- newRoom.name.set(roomConfig.name);
2334
- newRoom.balancingStrategy.set(roomConfig.balancingStrategy);
2335
- newRoom.public.set(roomConfig.public);
2336
- newRoom.maxPlayersPerShard.set(roomConfig.maxPlayersPerShard);
2337
- newRoom.minShards.set(roomConfig.minShards);
2338
- newRoom.maxShards.set(roomConfig.maxShards);
2339
- this.rooms()[roomId] = newRoom;
2340
- if (roomConfig.minShards > 0) {
2341
- for (let i = 0; i < roomConfig.minShards; i++) {
2342
- await this.createShard(roomId);
2343
- }
2344
- }
2345
- } else {
2346
- const room = this.rooms()[roomId];
2347
- room.balancingStrategy.set(roomConfig.balancingStrategy);
2348
- room.public.set(roomConfig.public);
2349
- room.maxPlayersPerShard.set(roomConfig.maxPlayersPerShard);
2350
- room.minShards.set(roomConfig.minShards);
2351
- room.maxShards.set(roomConfig.maxShards);
2352
- }
2353
- }
2354
- async updateShardStats(req, res) {
2355
- const body = await req.json();
2356
- const { shardId, connections, status } = body;
2357
- const shard = this.shards()[shardId];
2358
- if (!shard) {
2359
- return res.notFound(`Shard ${shardId} not found`);
2360
- }
2361
- shard.currentConnections.set(connections);
2362
- if (status) {
2363
- shard.status.set(status);
2364
- }
2365
- shard.lastHeartbeat.set(Date.now());
2366
- }
2367
- async scaleRoom(req, res) {
2368
- const data = await req.json();
2369
- const { targetShardCount, shardTemplate, roomId } = data;
2370
- const room = this.rooms()[roomId];
2371
- if (!room) {
2372
- return res.notFound(`Room ${roomId} does not exist`);
2373
- }
2374
- const roomShards = Object.values(this.shards()).filter((shard) => shard.roomId() === roomId);
2375
- const previousShardCount = roomShards.length;
2376
- if (room.maxShards() !== void 0 && targetShardCount > room.maxShards()) {
2377
- return res.badRequest(`Cannot scale beyond maximum allowed shards (${room.maxShards()})`, {
2378
- roomId,
2379
- currentShardCount: previousShardCount
2380
- });
2381
- }
2382
- if (targetShardCount < previousShardCount) {
2383
- const shardsToRemove = [
2384
- ...roomShards
2385
- ].sort((a, b) => {
2386
- if (a.status() === "draining" && b.status() !== "draining") return -1;
2387
- if (a.status() !== "draining" && b.status() === "draining") return 1;
2388
- return a.currentConnections() - b.currentConnections();
2389
- }).slice(0, previousShardCount - targetShardCount);
2390
- roomShards.filter((shard) => !shardsToRemove.some((s) => s.id === shard.id));
2391
- for (const shard of shardsToRemove) {
2392
- delete this.shards()[shard.id];
2393
- }
2394
- return;
2395
- }
2396
- if (targetShardCount > previousShardCount) {
2397
- for (let i = 0; i < targetShardCount - previousShardCount; i++) {
2398
- await this.createShard(roomId, shardTemplate?.urlTemplate, shardTemplate?.maxConnections);
2399
- }
2400
- }
2401
- }
2402
- async connect(req, res) {
2403
- try {
2404
- let data;
2405
- try {
2406
- const body = await req.text();
2407
- if (!body || body.trim() === "") {
2408
- return res.badRequest("Request body is empty");
2409
- }
2410
- data = JSON.parse(body);
2411
- } catch (parseError) {
2412
- return res.badRequest("Invalid JSON in request body");
2413
- }
2414
- if (!data.roomId) {
2415
- return res.badRequest("roomId parameter is required");
2416
- }
2417
- const autoCreate = data.autoCreate !== void 0 ? data.autoCreate : true;
2418
- const result = await this.findOptimalShard(data.roomId, autoCreate);
2419
- if ("error" in result) {
2420
- return res.notFound(result.error);
2421
- }
2422
- return res.success({
2423
- success: true,
2424
- shardId: result.shardId,
2425
- url: result.url
2426
- });
2427
- } catch (error) {
2428
- console.error("Error connecting to shard:", error);
2429
- return res.serverError();
2430
- }
2431
- }
2432
- async findOptimalShard(roomId, autoCreate = true) {
2433
- let room = this.rooms()[roomId];
2434
- if (!room) {
2435
- if (autoCreate) {
2436
- const mockRequest = {
2437
- json: /* @__PURE__ */ __name(async () => ({
2438
- name: roomId,
2439
- balancingStrategy: "round-robin",
2440
- public: true,
2441
- maxPlayersPerShard: this.defaultMaxConnectionsPerShard(),
2442
- minShards: 1,
2443
- maxShards: void 0
2444
- }), "json")
2445
- };
2446
- await this.registerRoom(mockRequest);
2447
- room = this.rooms()[roomId];
2448
- if (!room) {
2449
- return {
2450
- error: `Failed to create room ${roomId}`
2451
- };
2452
- }
2453
- } else {
2454
- return {
2455
- error: `Room ${roomId} does not exist`
2456
- };
2457
- }
2458
- }
2459
- const roomShards = Object.values(this.shards()).filter((shard) => shard.roomId() === roomId);
2460
- if (roomShards.length === 0) {
2461
- if (autoCreate) {
2462
- const newShard = await this.createShard(roomId);
2463
- if (newShard) {
2464
- return {
2465
- shardId: newShard.id,
2466
- url: newShard.url()
2467
- };
2468
- } else {
2469
- return {
2470
- error: `Failed to create shard for room ${roomId}`
2471
- };
2472
- }
2473
- } else {
2474
- return {
2475
- error: `No shards available for room ${roomId}`
2476
- };
2477
- }
2478
- }
2479
- const activeShards = roomShards.filter((shard) => shard && shard.status() === "active");
2480
- if (activeShards.length === 0) {
2481
- return {
2482
- error: `No active shards available for room ${roomId}`
2483
- };
2484
- }
2485
- const balancingStrategy = room.balancingStrategy();
2486
- let selectedShard;
2487
- switch (balancingStrategy) {
2488
- case "least-connections":
2489
- selectedShard = activeShards.reduce((min, shard) => shard.currentConnections() < min.currentConnections() ? shard : min, activeShards[0]);
2490
- break;
2491
- case "random":
2492
- selectedShard = activeShards[Math.floor(Math.random() * activeShards.length)];
2493
- break;
2494
- case "round-robin":
2495
- default:
2496
- const counter = this.rrCounters()[roomId] || 0;
2497
- const nextCounter = (counter + 1) % activeShards.length;
2498
- this.rrCounters()[roomId] = nextCounter;
2499
- selectedShard = activeShards[counter];
2500
- break;
2501
- }
2502
- return {
2503
- shardId: selectedShard.id,
2504
- url: selectedShard.url()
2505
- };
2506
- }
2507
- // Private methods
2508
- async createShard(roomId, urlTemplate, maxConnections) {
2509
- const room = this.rooms()[roomId];
2510
- if (!room) {
2511
- console.error(`Cannot create shard for non-existent room: ${roomId}`);
2512
- return null;
2513
- }
2514
- const shardId = `${roomId}:${Date.now()}-${Math.floor(Math.random() * 1e4)}`;
2515
- const template = urlTemplate || this.defaultShardUrlTemplate();
2516
- const url = template.replace("{shardId}", shardId).replace("{roomId}", roomId);
2517
- const max = maxConnections || room.maxPlayersPerShard();
2518
- const newShard = new ShardInfo();
2519
- newShard.id = shardId;
2520
- newShard.roomId.set(roomId);
2521
- newShard.url.set(url);
2522
- newShard.maxConnections.set(max);
2523
- newShard.currentConnections.set(0);
2524
- newShard.status.set("active");
2525
- newShard.lastHeartbeat.set(Date.now());
2526
- this.shards()[shardId] = newShard;
2527
- return newShard;
2528
- }
40
+ var clamp = {
41
+ direction: "all"
2529
42
  };
2530
- _ts_decorate([
2531
- sync(RoomConfig)
2532
- ], WorldRoom.prototype, "rooms", void 0);
2533
- _ts_decorate([
2534
- sync(ShardInfo)
2535
- ], WorldRoom.prototype, "shards", void 0);
2536
- _ts_decorate([
2537
- persist()
2538
- ], WorldRoom.prototype, "rrCounters", void 0);
2539
- _ts_decorate([
2540
- Request2({
2541
- path: "register-room",
2542
- method: "POST"
2543
- }),
2544
- Guard([
2545
- guardManageWorld
2546
- ]),
2547
- _ts_metadata("design:type", Function),
2548
- _ts_metadata("design:paramtypes", [
2549
- Object
2550
- ]),
2551
- _ts_metadata("design:returntype", Promise)
2552
- ], WorldRoom.prototype, "registerRoom", null);
2553
- _ts_decorate([
2554
- Request2({
2555
- path: "update-shard",
2556
- method: "POST"
2557
- }),
2558
- Guard([
2559
- guardManageWorld
2560
- ]),
2561
- _ts_metadata("design:type", Function),
2562
- _ts_metadata("design:paramtypes", [
2563
- Object ,
2564
- typeof ServerResponse === "undefined" ? Object : ServerResponse
2565
- ]),
2566
- _ts_metadata("design:returntype", Promise)
2567
- ], WorldRoom.prototype, "updateShardStats", null);
2568
- _ts_decorate([
2569
- Request2({
2570
- path: "scale-room",
2571
- method: "POST"
2572
- }),
2573
- Guard([
2574
- guardManageWorld
2575
- ]),
2576
- _ts_metadata("design:type", Function),
2577
- _ts_metadata("design:paramtypes", [
2578
- Object ,
2579
- typeof ServerResponse === "undefined" ? Object : ServerResponse
2580
- ]),
2581
- _ts_metadata("design:returntype", Promise)
2582
- ], WorldRoom.prototype, "scaleRoom", null);
2583
- _ts_decorate([
2584
- Request2({
2585
- path: "connect",
2586
- method: "POST"
2587
- }),
2588
- _ts_metadata("design:type", Function),
2589
- _ts_metadata("design:paramtypes", [
2590
- Object ,
2591
- typeof ServerResponse === "undefined" ? Object : ServerResponse
2592
- ]),
2593
- _ts_metadata("design:returntype", Promise)
2594
- ], WorldRoom.prototype, "connect", null);
2595
- WorldRoom = _ts_decorate([
2596
- Room({
2597
- path: "world-{worldId}",
2598
- maxUsers: 100,
2599
- throttleStorage: 2e3,
2600
- throttleSync: 500
2601
- }),
2602
- _ts_metadata("design:type", Function),
2603
- _ts_metadata("design:paramtypes", [
2604
- Object
2605
- ])
2606
- ], WorldRoom);
2607
-
2608
- // src/session.guard.ts
2609
- function createRequireSessionGuard(storage) {
2610
- return async (sender, value) => {
2611
- if (!sender || !sender.id) {
2612
- return false;
2613
- }
2614
- try {
2615
- const session = await storage.get(`session:${sender.id}`);
2616
- if (!session) {
2617
- return false;
2618
- }
2619
- const typedSession = session;
2620
- if (!typedSession.publicId) {
2621
- return false;
43
+ let $this = h(Canvas, { width: computed(() => engine.width()), height: computed(() => engine.height()) }, [h(Viewport, { worldWidth, worldHeight, clamp }, cond(sceneData, () => h(component$1))), loop(gui, gui => h(Container, { display: 'flex' }, cond(gui.display, () => h(gui.component, { ...gui.data(), dependencies: gui.dependencies, onFinish: (data) => {
44
+ onGuiFinish(gui, data);
45
+ }, onInteraction: (name, data) => {
46
+ onGuiInteraction(gui, name, data);
47
+ } }))))]);
48
+ return $this
2622
49
  }
2623
- return true;
2624
- } catch (error) {
2625
- console.error("Error checking session in requireSession guard:", error);
2626
- return false;
2627
- }
2628
- };
2629
- }
2630
- __name(createRequireSessionGuard, "createRequireSessionGuard");
2631
50
 
2632
- export { Action, ClientIo, Guard, MockConnection, Request2 as Request, Room, RoomGuard, Server, ServerIo, ServerResponse, Shard, WorldRoom, createRequireSessionGuard, request, testRoom, tick };
51
+ export { component as default };
2633
52
  //# sourceMappingURL=index26.js.map