@rpgjs/client 5.0.0-beta.5 → 5.0.0-beta.7

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 (133) hide show
  1. package/dist/Game/AnimationManager.d.ts +2 -2
  2. package/dist/Game/AnimationManager.js +18 -9
  3. package/dist/Game/AnimationManager.js.map +1 -1
  4. package/dist/Game/AnimationManager.spec.d.ts +1 -0
  5. package/dist/Game/Map.d.ts +7 -9
  6. package/dist/Game/Map.js +3 -3
  7. package/dist/Game/Map.js.map +1 -1
  8. package/dist/Game/Object.d.ts +28 -12
  9. package/dist/Game/Object.js +29 -4
  10. package/dist/Game/Object.js.map +1 -1
  11. package/dist/Gui/Gui.d.ts +2 -2
  12. package/dist/Gui/Gui.js +15 -15
  13. package/dist/Gui/Gui.js.map +1 -1
  14. package/dist/Gui/NotificationManager.d.ts +1 -1
  15. package/dist/Gui/NotificationManager.js.map +1 -1
  16. package/dist/Resource.js.map +1 -1
  17. package/dist/RpgClient.d.ts +22 -0
  18. package/dist/RpgClientEngine.d.ts +15 -12
  19. package/dist/RpgClientEngine.js +19 -4
  20. package/dist/RpgClientEngine.js.map +1 -1
  21. package/dist/Sound.js.map +1 -1
  22. package/dist/_virtual/{_@oxc-project_runtime@0.127.0 → _@oxc-project_runtime@0.128.0}/helpers/decorate.js +1 -1
  23. package/dist/_virtual/{_@oxc-project_runtime@0.127.0 → _@oxc-project_runtime@0.128.0}/helpers/decorateMetadata.js +1 -1
  24. package/dist/components/animations/animation.ce.js +2 -1
  25. package/dist/components/animations/animation.ce.js.map +1 -1
  26. package/dist/components/animations/hit.ce.js +2 -1
  27. package/dist/components/animations/hit.ce.js.map +1 -1
  28. package/dist/components/animations/index.js +4 -4
  29. package/dist/components/character.ce.js +57 -2
  30. package/dist/components/character.ce.js.map +1 -1
  31. package/dist/components/dynamics/parse-value.d.ts +1 -1
  32. package/dist/components/dynamics/parse-value.js.map +1 -1
  33. package/dist/components/dynamics/text.ce.js +2 -1
  34. package/dist/components/dynamics/text.ce.js.map +1 -1
  35. package/dist/components/gui/box.ce.js +2 -1
  36. package/dist/components/gui/box.ce.js.map +1 -1
  37. package/dist/components/gui/dialogbox/index.ce.js +2 -1
  38. package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
  39. package/dist/components/gui/gameover.ce.js +2 -1
  40. package/dist/components/gui/gameover.ce.js.map +1 -1
  41. package/dist/components/gui/hud/hud.ce.js +2 -1
  42. package/dist/components/gui/hud/hud.ce.js.map +1 -1
  43. package/dist/components/gui/menu/equip-menu.ce.js +2 -1
  44. package/dist/components/gui/menu/equip-menu.ce.js.map +1 -1
  45. package/dist/components/gui/menu/exit-menu.ce.js +2 -1
  46. package/dist/components/gui/menu/exit-menu.ce.js.map +1 -1
  47. package/dist/components/gui/menu/items-menu.ce.js +2 -1
  48. package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
  49. package/dist/components/gui/menu/main-menu.ce.js +14 -13
  50. package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
  51. package/dist/components/gui/menu/options-menu.ce.js +2 -1
  52. package/dist/components/gui/menu/options-menu.ce.js.map +1 -1
  53. package/dist/components/gui/menu/skills-menu.ce.js +2 -1
  54. package/dist/components/gui/menu/skills-menu.ce.js.map +1 -1
  55. package/dist/components/gui/mobile/index.d.ts +1 -1
  56. package/dist/components/gui/mobile/index.js +2 -2
  57. package/dist/components/gui/mobile/index.js.map +1 -1
  58. package/dist/components/gui/mobile/mobile.ce.js +2 -1
  59. package/dist/components/gui/mobile/mobile.ce.js.map +1 -1
  60. package/dist/components/gui/notification/notification.ce.js +9 -5
  61. package/dist/components/gui/notification/notification.ce.js.map +1 -1
  62. package/dist/components/gui/save-load.ce.js +2 -1
  63. package/dist/components/gui/save-load.ce.js.map +1 -1
  64. package/dist/components/gui/shop/shop.ce.js +2 -1
  65. package/dist/components/gui/shop/shop.ce.js.map +1 -1
  66. package/dist/components/gui/title-screen.ce.js +2 -1
  67. package/dist/components/gui/title-screen.ce.js.map +1 -1
  68. package/dist/components/prebuilt/hp-bar.ce.js +2 -1
  69. package/dist/components/prebuilt/hp-bar.ce.js.map +1 -1
  70. package/dist/components/prebuilt/light-halo.ce.js +2 -1
  71. package/dist/components/prebuilt/light-halo.ce.js.map +1 -1
  72. package/dist/components/scenes/canvas.ce.js +4 -3
  73. package/dist/components/scenes/canvas.ce.js.map +1 -1
  74. package/dist/components/scenes/draw-map.ce.js +2 -1
  75. package/dist/components/scenes/draw-map.ce.js.map +1 -1
  76. package/dist/components/scenes/event-layer.ce.js +5 -4
  77. package/dist/components/scenes/event-layer.ce.js.map +1 -1
  78. package/dist/core/inject.js +1 -1
  79. package/dist/core/inject.js.map +1 -1
  80. package/dist/core/setup.js +1 -1
  81. package/dist/core/setup.js.map +1 -1
  82. package/dist/index.js +20 -20
  83. package/dist/module.js +1 -1
  84. package/dist/module.js.map +1 -1
  85. package/dist/node_modules/.pnpm/{@signe_di@2.9.0 → @signe_di@2.10.0}/node_modules/@signe/di/dist/index.js +7 -117
  86. package/dist/node_modules/.pnpm/@signe_di@2.10.0/node_modules/@signe/di/dist/index.js.map +1 -0
  87. package/dist/node_modules/.pnpm/@signe_reactive@2.10.0/node_modules/@signe/reactive/dist/index.js +45 -0
  88. package/dist/node_modules/.pnpm/@signe_reactive@2.10.0/node_modules/@signe/reactive/dist/index.js.map +1 -0
  89. package/dist/node_modules/.pnpm/@signe_reactive@2.9.2/node_modules/@signe/reactive/dist/index.js +227 -0
  90. package/dist/node_modules/.pnpm/@signe_reactive@2.9.2/node_modules/@signe/reactive/dist/index.js.map +1 -0
  91. package/dist/node_modules/.pnpm/@signe_room@2.10.0/node_modules/@signe/room/dist/index.js +611 -0
  92. package/dist/node_modules/.pnpm/@signe_room@2.10.0/node_modules/@signe/room/dist/index.js.map +1 -0
  93. package/dist/node_modules/.pnpm/@signe_sync@2.10.0/node_modules/@signe/sync/dist/client/index.js +44 -0
  94. package/dist/node_modules/.pnpm/@signe_sync@2.10.0/node_modules/@signe/sync/dist/client/index.js.map +1 -0
  95. package/dist/node_modules/.pnpm/{@signe_sync@2.9.0 → @signe_sync@2.10.0}/node_modules/@signe/sync/dist/index.js +29 -136
  96. package/dist/node_modules/.pnpm/@signe_sync@2.10.0/node_modules/@signe/sync/dist/index.js.map +1 -0
  97. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js.map +1 -1
  98. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js.map +1 -1
  99. package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js.map +1 -1
  100. package/dist/presets/animation.js.map +1 -1
  101. package/dist/presets/faceset.js.map +1 -1
  102. package/dist/presets/icon.js.map +1 -1
  103. package/dist/presets/lpc.js.map +1 -1
  104. package/dist/presets/rmspritesheet.js.map +1 -1
  105. package/dist/services/AbstractSocket.js.map +1 -1
  106. package/dist/services/keyboardControls.js.map +1 -1
  107. package/dist/services/loadMap.js +1 -1
  108. package/dist/services/loadMap.js.map +1 -1
  109. package/dist/services/mmorpg.js +1 -1
  110. package/dist/services/mmorpg.js.map +1 -1
  111. package/dist/services/save.js.map +1 -1
  112. package/dist/services/standalone.js +1 -1
  113. package/dist/services/standalone.js.map +1 -1
  114. package/dist/utils/getEntityProp.js.map +1 -1
  115. package/package.json +10 -10
  116. package/src/Game/AnimationManager.spec.ts +30 -0
  117. package/src/Game/AnimationManager.ts +22 -10
  118. package/src/Game/Object.ts +46 -8
  119. package/src/RpgClient.ts +27 -0
  120. package/src/RpgClientEngine.ts +18 -2
  121. package/src/components/character.ce +72 -0
  122. package/dist/node_modules/.pnpm/@signe_di@2.9.0/node_modules/@signe/di/dist/index.js.map +0 -1
  123. package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js +0 -463
  124. package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js.map +0 -1
  125. package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js +0 -2191
  126. package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js.map +0 -1
  127. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js +0 -10
  128. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js.map +0 -1
  129. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js +0 -91
  130. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js.map +0 -1
  131. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/index.js.map +0 -1
  132. package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js +0 -14
  133. package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js.map +0 -1
@@ -0,0 +1,611 @@
1
+ import { id, persist, sync } from "../../../../../@signe_sync@2.10.0/node_modules/@signe/sync/dist/index.js";
2
+ import { z } from "../../../../../zod@3.24.2/node_modules/zod/lib/index.js";
3
+ import { signal } from "../../../../../@signe_reactive@2.9.2/node_modules/@signe/reactive/dist/index.js";
4
+ //#region ../../node_modules/.pnpm/@signe+room@2.10.0/node_modules/@signe/room/dist/index.js
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __decorateClass = (decorators, target, key, kind) => {
8
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
9
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
10
+ if (kind && result) __defProp(target, key, result);
11
+ return result;
12
+ };
13
+ function Request2(options, bodyValidation) {
14
+ return function(target, propertyKey) {
15
+ if (!target.constructor._requestMetadata) target.constructor._requestMetadata = /* @__PURE__ */ new Map();
16
+ const path = options.path.startsWith("/") ? options.path : `/${options.path}`;
17
+ const method = options.method || "GET";
18
+ const routeKey = `${method}:${path}`;
19
+ target.constructor._requestMetadata.set(routeKey, {
20
+ key: propertyKey,
21
+ path,
22
+ method,
23
+ bodyValidation
24
+ });
25
+ };
26
+ }
27
+ function Room(options) {
28
+ return function(target) {
29
+ target.path = options.path;
30
+ target.prototype.maxUsers = options.maxUsers;
31
+ target.prototype.throttleStorage = options.throttleStorage;
32
+ target.prototype.throttleSync = options.throttleSync;
33
+ target.prototype.sessionExpiryTime = options.sessionExpiryTime ?? 300 * 1e3;
34
+ if (options.guards) target["_roomGuards"] = options.guards;
35
+ };
36
+ }
37
+ function Guard(guards) {
38
+ return function(target, propertyKey, descriptor) {
39
+ if (!target.constructor["_actionGuards"]) target.constructor["_actionGuards"] = /* @__PURE__ */ new Map();
40
+ if (!Array.isArray(guards)) guards = [guards];
41
+ target.constructor["_actionGuards"].set(propertyKey, guards);
42
+ };
43
+ }
44
+ function generateShortUUID() {
45
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
46
+ let uuid = "";
47
+ for (let i = 0; i < 8; i++) {
48
+ const randomIndex = Math.floor(Math.random() * 62);
49
+ uuid += chars[randomIndex];
50
+ }
51
+ return uuid;
52
+ }
53
+ var Storage = class {
54
+ constructor() {
55
+ this.memory = /* @__PURE__ */ new Map();
56
+ }
57
+ async put(key, value) {
58
+ this.memory.set(key, value);
59
+ }
60
+ async get(key) {
61
+ return this.memory.get(key);
62
+ }
63
+ async delete(key) {
64
+ this.memory.delete(key);
65
+ }
66
+ async list() {
67
+ return this.memory;
68
+ }
69
+ };
70
+ z.object({
71
+ action: z.string(),
72
+ value: z.any()
73
+ });
74
+ async function request(room, path, options = { method: "GET" }) {
75
+ const url = new URL("http://localhost" + path);
76
+ const request2 = new Request(url.toString(), options);
77
+ return await room.onRequest(request2);
78
+ }
79
+ var MockPartyClient = class {
80
+ constructor(server, id2) {
81
+ this.server = server;
82
+ this.events = /* @__PURE__ */ new Map();
83
+ this.id = id2 || generateShortUUID();
84
+ this.conn = new MockConnection(this);
85
+ }
86
+ addEventListener(event, cb) {
87
+ if (!this.events.has(event)) this.events.set(event, []);
88
+ this.events.get(event).push(cb);
89
+ }
90
+ removeEventListener(event, cb) {
91
+ if (!this.events.has(event)) return;
92
+ const callbacks = this.events.get(event);
93
+ const index = callbacks.indexOf(cb);
94
+ if (index !== -1) callbacks.splice(index, 1);
95
+ if (callbacks.length === 0) this.events.delete(event);
96
+ }
97
+ _trigger(event, data) {
98
+ const callbacks = this.events.get(event);
99
+ if (callbacks) for (const cb of callbacks) cb(data);
100
+ }
101
+ send(data) {
102
+ return this.server.onMessage(JSON.stringify(data), this.conn);
103
+ }
104
+ };
105
+ var MockLobby = class {
106
+ constructor(server, lobbyId) {
107
+ this.server = server;
108
+ this.lobbyId = lobbyId;
109
+ }
110
+ socket(_init) {
111
+ return new MockPartyClient(this.server);
112
+ }
113
+ async connection(idOrOptions, maybeOptions) {
114
+ const id2 = typeof idOrOptions === "string" ? idOrOptions : idOrOptions?.id;
115
+ const options = (typeof idOrOptions === "string" ? maybeOptions : idOrOptions) || {};
116
+ return this.server.room.connection(this.server, id2, options);
117
+ }
118
+ fetch(url, options) {
119
+ const baseUrl = url.includes("shard") ? "" : "/parties/main/" + this.lobbyId;
120
+ return request(this.server, baseUrl + url, options);
121
+ }
122
+ };
123
+ var MockContext = class {
124
+ constructor(room, options = {}) {
125
+ this.room = room;
126
+ this.parties = { main: /* @__PURE__ */ new Map() };
127
+ const parties = options.parties || {};
128
+ if (options.partyFn) {
129
+ const serverCache = /* @__PURE__ */ new Map();
130
+ this.parties.main = { get: async (lobbyId) => {
131
+ if (!serverCache.has(lobbyId)) {
132
+ const server = await options.partyFn(lobbyId);
133
+ serverCache.set(lobbyId, new MockLobby(server, lobbyId));
134
+ }
135
+ return serverCache.get(lobbyId);
136
+ } };
137
+ } else for (let lobbyId in parties) {
138
+ const server = parties[lobbyId](room);
139
+ this.parties.main.set(lobbyId, new MockLobby(server, lobbyId));
140
+ }
141
+ }
142
+ };
143
+ var MockPartyRoom = class {
144
+ constructor(id2, options = {}) {
145
+ this.id = id2;
146
+ this.clients = /* @__PURE__ */ new Map();
147
+ this.storage = new Storage();
148
+ this.env = {};
149
+ this.id = id2 || generateShortUUID();
150
+ this.context = new MockContext(this, {
151
+ parties: options.parties,
152
+ partyFn: options.partyFn
153
+ });
154
+ this.env = options.env || {};
155
+ }
156
+ async connection(server, id2, opts) {
157
+ const socket = new MockPartyClient(server, id2);
158
+ const url = new URL("http://localhost");
159
+ if (opts?.query) for (const [key, value] of Object.entries(opts.query)) url.searchParams.set(key, String(value));
160
+ const request2 = new Request(url.toString(), {
161
+ method: "GET",
162
+ headers: {
163
+ "Content-Type": "application/json",
164
+ ...opts?.headers || {}
165
+ }
166
+ });
167
+ await server.onConnect(socket.conn, { request: request2 });
168
+ this.clients.set(socket.id, socket);
169
+ return socket;
170
+ }
171
+ broadcast(data) {
172
+ this.clients.forEach((client) => {
173
+ client._trigger("message", data);
174
+ });
175
+ }
176
+ getConnection(id2) {
177
+ return this.clients.get(id2);
178
+ }
179
+ getConnections() {
180
+ return Array.from(this.clients.values()).map((client) => client.conn);
181
+ }
182
+ clear() {
183
+ this.clients.clear();
184
+ }
185
+ };
186
+ var MockConnection = class {
187
+ constructor(client) {
188
+ this.client = client;
189
+ this.state = {};
190
+ this.server = client.server;
191
+ this.id = client.id;
192
+ }
193
+ setState(value) {
194
+ this.state = value;
195
+ }
196
+ send(data) {
197
+ this.client._trigger("message", data);
198
+ }
199
+ close() {
200
+ this.server.onClose(this);
201
+ }
202
+ };
203
+ var ServerIo = MockPartyRoom;
204
+ var ClientIo = MockPartyClient;
205
+ var JWTAuth = class {
206
+ /**
207
+ * Constructor for the JWTAuth class
208
+ * @param {string} secret - The secret key used for signing and verifying tokens
209
+ */
210
+ constructor(secret) {
211
+ if (!secret || typeof secret !== "string") throw new Error("Secret is required and must be a string");
212
+ this.secret = secret;
213
+ this.encoder = new TextEncoder();
214
+ this.decoder = new TextDecoder();
215
+ }
216
+ /**
217
+ * Convert the secret to a CryptoKey for HMAC operations
218
+ * @returns {Promise<CryptoKey>} - The CryptoKey for HMAC operations
219
+ */
220
+ async getSecretKey() {
221
+ const keyData = this.encoder.encode(this.secret);
222
+ return await crypto.subtle.importKey("raw", keyData, {
223
+ name: "HMAC",
224
+ hash: { name: "SHA-256" }
225
+ }, false, ["sign", "verify"]);
226
+ }
227
+ /**
228
+ * Base64Url encode a buffer
229
+ * @param {ArrayBuffer} buffer - The buffer to encode
230
+ * @returns {string} - The base64url encoded string
231
+ */
232
+ base64UrlEncode(buffer) {
233
+ return btoa(String.fromCharCode(...new Uint8Array(buffer))).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
234
+ }
235
+ /**
236
+ * Base64Url decode a string
237
+ * @param {string} base64Url - The base64url encoded string
238
+ * @returns {ArrayBuffer} - The decoded buffer
239
+ */
240
+ base64UrlDecode(base64Url) {
241
+ const padding = "=".repeat((4 - base64Url.length % 4) % 4);
242
+ const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/") + padding;
243
+ const rawData = atob(base64);
244
+ const buffer = new Uint8Array(rawData.length);
245
+ for (let i = 0; i < rawData.length; i++) buffer[i] = rawData.charCodeAt(i);
246
+ return buffer.buffer;
247
+ }
248
+ /**
249
+ * Sign a payload and create a JWT token
250
+ * @param {JWTPayload} payload - The payload to include in the token
251
+ * @param {JWTOptions} [options={}] - Options for the token
252
+ * @param {string | number} [options.expiresIn='1h'] - Token expiration time
253
+ * @returns {Promise<string>} - The JWT token
254
+ */
255
+ async sign(payload, options = {}) {
256
+ if (!payload || typeof payload !== "object") throw new Error("Payload must be an object");
257
+ const expiresIn = options.expiresIn || "1h";
258
+ let exp;
259
+ if (typeof expiresIn === "number") exp = Math.floor(Date.now() / 1e3) + expiresIn;
260
+ else if (typeof expiresIn === "string") {
261
+ const match = expiresIn.match(/^(\d+)([smhd])$/);
262
+ if (match) {
263
+ const value = parseInt(match[1]);
264
+ const unit = match[2];
265
+ const seconds = {
266
+ "s": value,
267
+ "m": value * 60,
268
+ "h": value * 60 * 60,
269
+ "d": value * 60 * 60 * 24
270
+ }[unit];
271
+ exp = Math.floor(Date.now() / 1e3) + seconds;
272
+ } else throw new Error("Invalid expiresIn format. Use a number (seconds) or a string like \"1h\", \"30m\", etc.");
273
+ }
274
+ const fullPayload = {
275
+ ...payload,
276
+ iat: Math.floor(Date.now() / 1e3),
277
+ exp
278
+ };
279
+ const signatureBase = `${this.base64UrlEncode(this.encoder.encode(JSON.stringify({
280
+ alg: "HS256",
281
+ typ: "JWT"
282
+ })))}.${this.base64UrlEncode(this.encoder.encode(JSON.stringify(fullPayload)))}`;
283
+ const key = await this.getSecretKey();
284
+ const signature = await crypto.subtle.sign({ name: "HMAC" }, key, this.encoder.encode(signatureBase));
285
+ return `${signatureBase}.${this.base64UrlEncode(signature)}`;
286
+ }
287
+ /**
288
+ * Verify a JWT token and return the decoded payload
289
+ * @param {string} token - The JWT token to verify
290
+ * @returns {Promise<JWTPayload>} - The decoded payload if verification succeeds
291
+ * @throws {Error} - If verification fails
292
+ */
293
+ async verify(token) {
294
+ if (!token || typeof token !== "string") throw new Error("Token is required and must be a string");
295
+ const parts = token.split(".");
296
+ if (parts.length !== 3) throw new Error("Invalid token format");
297
+ const [encodedHeader, encodedPayload, encodedSignature] = parts;
298
+ try {
299
+ const header = JSON.parse(this.decoder.decode(this.base64UrlDecode(encodedHeader)));
300
+ const payload = JSON.parse(this.decoder.decode(this.base64UrlDecode(encodedPayload)));
301
+ if (header.alg !== "HS256") throw new Error(`Unsupported algorithm: ${header.alg}`);
302
+ const now = Math.floor(Date.now() / 1e3);
303
+ if (payload.exp && payload.exp < now) throw new Error("Token has expired");
304
+ const key = await this.getSecretKey();
305
+ const signatureBase = `${encodedHeader}.${encodedPayload}`;
306
+ const signature = this.base64UrlDecode(encodedSignature);
307
+ if (!await crypto.subtle.verify({ name: "HMAC" }, key, signature, this.encoder.encode(signatureBase))) throw new Error("Invalid signature");
308
+ return payload;
309
+ } catch (error) {
310
+ if (error instanceof Error) throw new Error(`Token verification failed: ${error.message}`);
311
+ throw new Error("Token verification failed: Unknown error");
312
+ }
313
+ }
314
+ };
315
+ var guardManageWorld = async (_, req, room) => {
316
+ const tokenShard = req.headers.get("x-access-shard");
317
+ if (tokenShard) {
318
+ if (tokenShard !== room.env.SHARD_SECRET) return false;
319
+ return true;
320
+ }
321
+ const url = new URL(req.url);
322
+ const token = req.headers.get("Authorization") ?? url.searchParams.get("world-auth-token");
323
+ if (!token) return false;
324
+ const jwt = new JWTAuth(room.env.AUTH_JWT_SECRET);
325
+ try {
326
+ if (!await jwt.verify(token)) return false;
327
+ } catch (error) {
328
+ return false;
329
+ }
330
+ return true;
331
+ };
332
+ var MAX_PLAYERS_PER_SHARD = 75;
333
+ z.object({
334
+ name: z.string(),
335
+ balancingStrategy: z.enum([
336
+ "round-robin",
337
+ "least-connections",
338
+ "random"
339
+ ]),
340
+ public: z.boolean(),
341
+ maxPlayersPerShard: z.number().int().positive(),
342
+ minShards: z.number().int().min(0),
343
+ maxShards: z.number().int().positive().optional()
344
+ });
345
+ z.object({
346
+ shardId: z.string(),
347
+ roomId: z.string(),
348
+ url: z.string().url(),
349
+ maxConnections: z.number().int().positive()
350
+ });
351
+ z.object({
352
+ connections: z.number().int().min(0),
353
+ status: z.enum([
354
+ "active",
355
+ "maintenance",
356
+ "draining"
357
+ ]).optional()
358
+ });
359
+ z.object({
360
+ roomId: z.string(),
361
+ targetShardCount: z.number().int().positive(),
362
+ shardTemplate: z.object({
363
+ urlTemplate: z.string(),
364
+ maxConnections: z.number().int().positive()
365
+ }).optional()
366
+ });
367
+ var RoomConfig = class {
368
+ constructor() {
369
+ this.name = signal("");
370
+ this.balancingStrategy = signal("round-robin");
371
+ this.public = signal(true);
372
+ this.maxPlayersPerShard = signal(MAX_PLAYERS_PER_SHARD);
373
+ this.minShards = signal(1);
374
+ this.maxShards = signal(void 0);
375
+ }
376
+ };
377
+ __decorateClass([id()], RoomConfig.prototype, "id", 2);
378
+ __decorateClass([sync()], RoomConfig.prototype, "name", 2);
379
+ __decorateClass([sync()], RoomConfig.prototype, "balancingStrategy", 2);
380
+ __decorateClass([sync()], RoomConfig.prototype, "public", 2);
381
+ __decorateClass([sync()], RoomConfig.prototype, "maxPlayersPerShard", 2);
382
+ __decorateClass([sync()], RoomConfig.prototype, "minShards", 2);
383
+ __decorateClass([sync()], RoomConfig.prototype, "maxShards", 2);
384
+ var ShardInfo = class {
385
+ constructor() {
386
+ this.roomId = signal("");
387
+ this.url = signal("");
388
+ this.currentConnections = signal(0);
389
+ this.maxConnections = signal(MAX_PLAYERS_PER_SHARD);
390
+ this.status = signal("active");
391
+ this.lastHeartbeat = signal(0);
392
+ }
393
+ };
394
+ __decorateClass([id()], ShardInfo.prototype, "id", 2);
395
+ __decorateClass([sync()], ShardInfo.prototype, "roomId", 2);
396
+ __decorateClass([sync()], ShardInfo.prototype, "url", 2);
397
+ __decorateClass([sync({ persist: false })], ShardInfo.prototype, "currentConnections", 2);
398
+ __decorateClass([sync()], ShardInfo.prototype, "maxConnections", 2);
399
+ __decorateClass([sync()], ShardInfo.prototype, "status", 2);
400
+ __decorateClass([sync()], ShardInfo.prototype, "lastHeartbeat", 2);
401
+ var WorldRoom = class {
402
+ constructor(room) {
403
+ this.room = room;
404
+ this.rooms = signal({});
405
+ this.shards = signal({});
406
+ this.rrCounters = signal({});
407
+ this.defaultShardUrlTemplate = signal("{shardId}");
408
+ this.defaultMaxConnectionsPerShard = signal(MAX_PLAYERS_PER_SHARD);
409
+ const { AUTH_JWT_SECRET, SHARD_SECRET } = this.room.env;
410
+ if (!AUTH_JWT_SECRET) throw new Error("AUTH_JWT_SECRET env variable is not set");
411
+ if (!SHARD_SECRET) throw new Error("SHARD_SECRET env variable is not set");
412
+ }
413
+ async onJoin(user, conn, ctx) {
414
+ const canConnect = await guardManageWorld(user, ctx.request, this.room);
415
+ conn.setState({
416
+ ...conn.state,
417
+ isAdmin: canConnect
418
+ });
419
+ }
420
+ interceptorPacket(_, obj, conn) {
421
+ if (!conn.state["isAdmin"]) return null;
422
+ return obj;
423
+ }
424
+ cleanupInactiveShards() {
425
+ const now = Date.now();
426
+ const timeout = 300 * 1e3;
427
+ const shardsValue = this.shards();
428
+ Object.values(shardsValue).forEach((shard) => {
429
+ if (now - shard.lastHeartbeat() > timeout) delete this.shards()[shard.id];
430
+ });
431
+ setTimeout(() => this.cleanupInactiveShards(), 6e4);
432
+ }
433
+ async registerRoom(req) {
434
+ const roomConfig = await req.json();
435
+ const roomId = roomConfig.name;
436
+ if (!this.rooms()[roomId]) {
437
+ const newRoom = new RoomConfig();
438
+ newRoom.id = roomId;
439
+ newRoom.name.set(roomConfig.name);
440
+ newRoom.balancingStrategy.set(roomConfig.balancingStrategy);
441
+ newRoom.public.set(roomConfig.public);
442
+ newRoom.maxPlayersPerShard.set(roomConfig.maxPlayersPerShard);
443
+ newRoom.minShards.set(roomConfig.minShards);
444
+ newRoom.maxShards.set(roomConfig.maxShards);
445
+ this.rooms()[roomId] = newRoom;
446
+ if (roomConfig.minShards > 0) for (let i = 0; i < roomConfig.minShards; i++) await this.createShard(roomId);
447
+ } else {
448
+ const room = this.rooms()[roomId];
449
+ room.balancingStrategy.set(roomConfig.balancingStrategy);
450
+ room.public.set(roomConfig.public);
451
+ room.maxPlayersPerShard.set(roomConfig.maxPlayersPerShard);
452
+ room.minShards.set(roomConfig.minShards);
453
+ room.maxShards.set(roomConfig.maxShards);
454
+ }
455
+ }
456
+ async updateShardStats(req, res) {
457
+ const { shardId, connections, status } = await req.json();
458
+ const shard = this.shards()[shardId];
459
+ if (!shard) return res.notFound(`Shard ${shardId} not found`);
460
+ shard.currentConnections.set(connections);
461
+ if (status) shard.status.set(status);
462
+ shard.lastHeartbeat.set(Date.now());
463
+ }
464
+ async scaleRoom(req, res) {
465
+ const { targetShardCount, shardTemplate, roomId } = await req.json();
466
+ const room = this.rooms()[roomId];
467
+ if (!room) return res.notFound(`Room ${roomId} does not exist`);
468
+ const roomShards = Object.values(this.shards()).filter((shard) => shard.roomId() === roomId);
469
+ const previousShardCount = roomShards.length;
470
+ if (room.maxShards() !== void 0 && targetShardCount > room.maxShards()) return res.badRequest(`Cannot scale beyond maximum allowed shards (${room.maxShards()})`, {
471
+ roomId,
472
+ currentShardCount: previousShardCount
473
+ });
474
+ if (targetShardCount < previousShardCount) {
475
+ const shardsToRemove = [...roomShards].sort((a, b) => {
476
+ if (a.status() === "draining" && b.status() !== "draining") return -1;
477
+ if (a.status() !== "draining" && b.status() === "draining") return 1;
478
+ return a.currentConnections() - b.currentConnections();
479
+ }).slice(0, previousShardCount - targetShardCount);
480
+ roomShards.filter((shard) => !shardsToRemove.some((s) => s.id === shard.id));
481
+ for (const shard of shardsToRemove) delete this.shards()[shard.id];
482
+ return;
483
+ }
484
+ if (targetShardCount > previousShardCount) {
485
+ const newShards = [];
486
+ for (let i = 0; i < targetShardCount - previousShardCount; i++) {
487
+ const newShard = await this.createShard(roomId, shardTemplate?.urlTemplate, shardTemplate?.maxConnections);
488
+ if (newShard) newShards.push(newShard);
489
+ }
490
+ }
491
+ }
492
+ async connect(req, res) {
493
+ try {
494
+ let data;
495
+ try {
496
+ const body = await req.text();
497
+ if (!body || body.trim() === "") return res.badRequest("Request body is empty");
498
+ data = JSON.parse(body);
499
+ } catch (parseError) {
500
+ return res.badRequest("Invalid JSON in request body");
501
+ }
502
+ if (!data.roomId) return res.badRequest("roomId parameter is required");
503
+ const autoCreate = data.autoCreate !== void 0 ? data.autoCreate : true;
504
+ const result = await this.findOptimalShard(data.roomId, autoCreate);
505
+ if ("error" in result) return res.notFound(result.error);
506
+ return res.success({
507
+ success: true,
508
+ shardId: result.shardId,
509
+ url: result.url
510
+ });
511
+ } catch (error) {
512
+ console.error("Error connecting to shard:", error);
513
+ return res.serverError();
514
+ }
515
+ }
516
+ async findOptimalShard(roomId, autoCreate = true) {
517
+ let room = this.rooms()[roomId];
518
+ if (!room) if (autoCreate) {
519
+ await this.registerRoom({ json: async () => ({
520
+ name: roomId,
521
+ balancingStrategy: "round-robin",
522
+ public: true,
523
+ maxPlayersPerShard: this.defaultMaxConnectionsPerShard(),
524
+ minShards: 1,
525
+ maxShards: void 0
526
+ }) });
527
+ room = this.rooms()[roomId];
528
+ if (!room) return { error: `Failed to create room ${roomId}` };
529
+ } else return { error: `Room ${roomId} does not exist` };
530
+ const roomShards = Object.values(this.shards()).filter((shard) => shard.roomId() === roomId);
531
+ if (roomShards.length === 0) if (autoCreate) {
532
+ const newShard = await this.createShard(roomId);
533
+ if (newShard) return {
534
+ shardId: newShard.id,
535
+ url: newShard.url()
536
+ };
537
+ else return { error: `Failed to create shard for room ${roomId}` };
538
+ } else return { error: `No shards available for room ${roomId}` };
539
+ const activeShards = roomShards.filter((shard) => shard && shard.status() === "active");
540
+ if (activeShards.length === 0) return { error: `No active shards available for room ${roomId}` };
541
+ const balancingStrategy = room.balancingStrategy();
542
+ let selectedShard;
543
+ switch (balancingStrategy) {
544
+ case "least-connections":
545
+ selectedShard = activeShards.reduce((min, shard) => shard.currentConnections() < min.currentConnections() ? shard : min, activeShards[0]);
546
+ break;
547
+ case "random":
548
+ selectedShard = activeShards[Math.floor(Math.random() * activeShards.length)];
549
+ break;
550
+ default:
551
+ const counter = this.rrCounters()[roomId] || 0;
552
+ const nextCounter = (counter + 1) % activeShards.length;
553
+ this.rrCounters()[roomId] = nextCounter;
554
+ selectedShard = activeShards[counter];
555
+ break;
556
+ }
557
+ return {
558
+ shardId: selectedShard.id,
559
+ url: selectedShard.url()
560
+ };
561
+ }
562
+ async createShard(roomId, urlTemplate, maxConnections) {
563
+ const room = this.rooms()[roomId];
564
+ if (!room) {
565
+ console.error(`Cannot create shard for non-existent room: ${roomId}`);
566
+ return null;
567
+ }
568
+ const shardId = `${roomId}:${Date.now()}-${Math.floor(Math.random() * 1e4)}`;
569
+ const url = (urlTemplate || this.defaultShardUrlTemplate()).replace("{shardId}", shardId).replace("{roomId}", roomId);
570
+ const max = maxConnections || room.maxPlayersPerShard();
571
+ const newShard = new ShardInfo();
572
+ newShard.id = shardId;
573
+ newShard.roomId.set(roomId);
574
+ newShard.url.set(url);
575
+ newShard.maxConnections.set(max);
576
+ newShard.currentConnections.set(0);
577
+ newShard.status.set("active");
578
+ newShard.lastHeartbeat.set(Date.now());
579
+ this.shards()[shardId] = newShard;
580
+ return newShard;
581
+ }
582
+ };
583
+ __decorateClass([sync(RoomConfig)], WorldRoom.prototype, "rooms", 2);
584
+ __decorateClass([sync(ShardInfo)], WorldRoom.prototype, "shards", 2);
585
+ __decorateClass([persist()], WorldRoom.prototype, "rrCounters", 2);
586
+ __decorateClass([Request2({
587
+ path: "register-room",
588
+ method: "POST"
589
+ }), Guard([guardManageWorld])], WorldRoom.prototype, "registerRoom", 1);
590
+ __decorateClass([Request2({
591
+ path: "update-shard",
592
+ method: "POST"
593
+ }), Guard([guardManageWorld])], WorldRoom.prototype, "updateShardStats", 1);
594
+ __decorateClass([Request2({
595
+ path: "scale-room",
596
+ method: "POST"
597
+ }), Guard([guardManageWorld])], WorldRoom.prototype, "scaleRoom", 1);
598
+ __decorateClass([Request2({
599
+ path: "connect",
600
+ method: "POST"
601
+ })], WorldRoom.prototype, "connect", 1);
602
+ WorldRoom = __decorateClass([Room({
603
+ path: "world-{worldId}",
604
+ maxUsers: 100,
605
+ throttleStorage: 2e3,
606
+ throttleSync: 500
607
+ })], WorldRoom);
608
+ //#endregion
609
+ export { ClientIo, ServerIo };
610
+
611
+ //# sourceMappingURL=index.js.map