@signe/room 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +195 -6
- package/dist/index.js +468 -57
- package/dist/index.js.map +1 -1
- package/examples/game/.vscode/launch.json +11 -0
- package/examples/game/.vscode/settings.json +11 -0
- package/examples/game/README.md +40 -0
- package/examples/game/app/client.tsx +40 -0
- package/examples/game/app/components/Counter.tsx +44 -0
- package/examples/game/app/styles.css +31 -0
- package/examples/game/package.json +20 -0
- package/examples/game/party/game.room.ts +12 -0
- package/examples/game/party/server.ts +12 -0
- package/examples/game/partykit.json +10 -0
- package/examples/game/public/favicon.ico +0 -0
- package/examples/game/public/index.html +27 -0
- package/examples/game/public/normalize.css +351 -0
- package/examples/game/shared/room.schema.ts +6 -0
- package/examples/game/tsconfig.json +109 -0
- package/package.json +2 -2
- package/src/decorators.ts +11 -2
- package/src/index.ts +1 -0
- package/src/mock.ts +70 -0
- package/src/server.ts +272 -48
- package/src/utils.ts +150 -24
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/decorators.ts
|
|
2
|
-
function
|
|
2
|
+
function Action(name, bodyValidation) {
|
|
3
3
|
return function(target, propertyKey) {
|
|
4
4
|
if (!target.constructor._actionMetadata) {
|
|
5
5
|
target.constructor._actionMetadata = /* @__PURE__ */ new Map();
|
|
@@ -15,23 +15,261 @@ function Room(options) {
|
|
|
15
15
|
target.path = options.path;
|
|
16
16
|
target.maxUsers = options.maxUsers;
|
|
17
17
|
target.throttleStorage = options.throttleStorage;
|
|
18
|
+
target.throttleSync = options.throttleSync;
|
|
18
19
|
};
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
// ../sync/src/utils.ts
|
|
23
|
+
function isClass(obj) {
|
|
24
|
+
return typeof obj === "function" && obj.prototype && obj.prototype.constructor === obj;
|
|
25
|
+
}
|
|
26
|
+
var isObject = (item) => item && typeof item === "object" && !Array.isArray(item) && item !== null;
|
|
27
|
+
function generateShortUUID() {
|
|
28
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
29
|
+
let uuid = "";
|
|
30
|
+
for (let i = 0; i < 8; i++) {
|
|
31
|
+
const randomIndex = Math.floor(Math.random() * chars.length);
|
|
32
|
+
uuid += chars[randomIndex];
|
|
33
|
+
}
|
|
34
|
+
return uuid;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/mock.ts
|
|
38
|
+
var MockPartySocket = class {
|
|
39
|
+
constructor() {
|
|
40
|
+
this.events = /* @__PURE__ */ new Map();
|
|
41
|
+
this.id = generateShortUUID();
|
|
42
|
+
}
|
|
43
|
+
addEventListener(event, cb) {
|
|
44
|
+
this.events.set(event, cb);
|
|
45
|
+
}
|
|
46
|
+
removeEventListener(event, cb) {
|
|
47
|
+
this.events.delete(event);
|
|
48
|
+
}
|
|
49
|
+
_trigger(event, data) {
|
|
50
|
+
this.events.get(event)?.(data);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var MockStorage = class {
|
|
54
|
+
constructor() {
|
|
55
|
+
this.storage = /* @__PURE__ */ new Map();
|
|
56
|
+
}
|
|
57
|
+
async get(key) {
|
|
58
|
+
return this.storage.get(key);
|
|
59
|
+
}
|
|
60
|
+
async put(key, value) {
|
|
61
|
+
this.storage.set(key, value);
|
|
62
|
+
}
|
|
63
|
+
async list() {
|
|
64
|
+
return this.storage;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var MockPartyRoom = class {
|
|
68
|
+
constructor(id) {
|
|
69
|
+
this.id = id;
|
|
70
|
+
this.clients = /* @__PURE__ */ new Map();
|
|
71
|
+
this.storage = new MockStorage();
|
|
72
|
+
this.id = id || generateShortUUID();
|
|
73
|
+
}
|
|
74
|
+
connection(client) {
|
|
75
|
+
const socket = new MockPartySocket();
|
|
76
|
+
this.clients.set(socket.id, client);
|
|
77
|
+
client.id = socket.id;
|
|
78
|
+
}
|
|
79
|
+
broadcast(data) {
|
|
80
|
+
this.clients.forEach((client) => {
|
|
81
|
+
client._trigger("message", data);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
clear() {
|
|
85
|
+
this.clients.clear();
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
var MockConnection = class {
|
|
89
|
+
constructor() {
|
|
90
|
+
this.state = {};
|
|
91
|
+
}
|
|
92
|
+
setState(value) {
|
|
93
|
+
this.state = value;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
var ServerIo = MockPartyRoom;
|
|
97
|
+
var ClientIo = MockPartySocket;
|
|
98
|
+
|
|
21
99
|
// src/server.ts
|
|
22
|
-
import { createStatesSnapshot, getByPath, load, syncClass } from "@signe/sync";
|
|
23
100
|
import { dset as dset2 } from "dset";
|
|
24
101
|
import z from "zod";
|
|
25
102
|
|
|
103
|
+
// ../sync/src/core.ts
|
|
104
|
+
import {
|
|
105
|
+
ArraySubject,
|
|
106
|
+
ObjectSubject,
|
|
107
|
+
isSignal
|
|
108
|
+
} from "@signe/reactive";
|
|
109
|
+
var syncClass = (instance, options = {}) => {
|
|
110
|
+
const cacheSync = /* @__PURE__ */ new Map();
|
|
111
|
+
const cachePersist = /* @__PURE__ */ new Set();
|
|
112
|
+
instance.$valuesChanges = {
|
|
113
|
+
set: (path, value) => {
|
|
114
|
+
cacheSync.set(path, value);
|
|
115
|
+
options.onSync?.(cacheSync);
|
|
116
|
+
},
|
|
117
|
+
setPersist: (path) => {
|
|
118
|
+
if (path == "") path = ".";
|
|
119
|
+
cachePersist.add(path);
|
|
120
|
+
options.onPersist?.(cachePersist);
|
|
121
|
+
},
|
|
122
|
+
has: (path) => {
|
|
123
|
+
return cacheSync.has(path);
|
|
124
|
+
},
|
|
125
|
+
get: (path) => {
|
|
126
|
+
return cacheSync.get(path);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
createSyncClass(instance);
|
|
130
|
+
};
|
|
131
|
+
function createStatesSnapshot(instance) {
|
|
132
|
+
let persistObject = {};
|
|
133
|
+
if (instance.$snapshot) {
|
|
134
|
+
for (const key of instance.$snapshot.keys()) {
|
|
135
|
+
const signal = instance.$snapshot.get(key);
|
|
136
|
+
const persist = signal.options.persist ?? true;
|
|
137
|
+
let value = signal();
|
|
138
|
+
if (isObject(value) || Array.isArray(value)) {
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
if (persist) {
|
|
142
|
+
persistObject[key] = value;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return persistObject;
|
|
147
|
+
}
|
|
148
|
+
function setMetadata(target, key, value) {
|
|
149
|
+
const meta = target.constructor._propertyMetadata;
|
|
150
|
+
const propId = meta?.get(key);
|
|
151
|
+
if (propId) {
|
|
152
|
+
if (isSignal(target[propId])) {
|
|
153
|
+
target[propId].set(value);
|
|
154
|
+
} else {
|
|
155
|
+
target[propId] = value;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
var createSyncClass = (currentClass, parentKey = null, parentClass = null, path = "") => {
|
|
160
|
+
currentClass.$path = path;
|
|
161
|
+
if (parentClass) {
|
|
162
|
+
currentClass.$valuesChanges = parentClass.$valuesChanges;
|
|
163
|
+
}
|
|
164
|
+
if (parentKey) {
|
|
165
|
+
setMetadata(currentClass, "id", parentKey);
|
|
166
|
+
}
|
|
167
|
+
if (currentClass.$snapshot) {
|
|
168
|
+
for (const key of currentClass.$snapshot.keys()) {
|
|
169
|
+
const signal = currentClass.$snapshot.get(key);
|
|
170
|
+
const syncToClient = signal.options.syncToClient ?? true;
|
|
171
|
+
const persist = signal.options.persist ?? true;
|
|
172
|
+
let value = signal();
|
|
173
|
+
if (isObject(value) || Array.isArray(value)) {
|
|
174
|
+
value = { ...value };
|
|
175
|
+
}
|
|
176
|
+
const newPath = (path ? path + "." : "") + key;
|
|
177
|
+
if (syncToClient) {
|
|
178
|
+
currentClass.$valuesChanges.set(newPath, value);
|
|
179
|
+
}
|
|
180
|
+
if (persist) {
|
|
181
|
+
if (parentClass) currentClass.$valuesChanges.setPersist(path);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// ../sync/src/load.ts
|
|
188
|
+
import { isSignal as isSignal2 } from "@signe/reactive";
|
|
189
|
+
function load(rootInstance, values, valueIsObject) {
|
|
190
|
+
if (valueIsObject) {
|
|
191
|
+
loadFromObject(rootInstance, values);
|
|
192
|
+
} else {
|
|
193
|
+
loadFromPaths(rootInstance, values);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function loadFromPaths(rootInstance, values) {
|
|
197
|
+
for (const [path, value] of Object.entries(values)) {
|
|
198
|
+
const parts = path.split(".");
|
|
199
|
+
loadValue(rootInstance, parts, value);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function loadFromObject(rootInstance, values, currentPath = "") {
|
|
203
|
+
for (const [key, value] of Object.entries(values)) {
|
|
204
|
+
const newPath = currentPath ? `${currentPath}.${key}` : key;
|
|
205
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
206
|
+
loadFromObject(rootInstance, value, newPath);
|
|
207
|
+
} else {
|
|
208
|
+
const parts = newPath.split(".");
|
|
209
|
+
loadValue(rootInstance, parts, value);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function loadValue(rootInstance, parts, value) {
|
|
214
|
+
let current = rootInstance;
|
|
215
|
+
for (let i = 0; i < parts.length; i++) {
|
|
216
|
+
const part = parts[i];
|
|
217
|
+
if (i === parts.length - 1) {
|
|
218
|
+
if (value == "$delete") {
|
|
219
|
+
if (isSignal2(current)) {
|
|
220
|
+
current = current();
|
|
221
|
+
}
|
|
222
|
+
Reflect.deleteProperty(current, part);
|
|
223
|
+
} else if (current[part]?._subject) {
|
|
224
|
+
current[part].set(value);
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
if (isSignal2(current)) {
|
|
228
|
+
current = current();
|
|
229
|
+
}
|
|
230
|
+
const currentValue = current[part];
|
|
231
|
+
if (currentValue === void 0) {
|
|
232
|
+
const parentInstance = getByPath(
|
|
233
|
+
rootInstance,
|
|
234
|
+
parts.slice(0, i).join(".")
|
|
235
|
+
);
|
|
236
|
+
const classType = parentInstance?.options?.classType;
|
|
237
|
+
if (classType) {
|
|
238
|
+
current[part] = !isClass(classType) ? classType(part) : new classType();
|
|
239
|
+
setMetadata(current[part], "id", part);
|
|
240
|
+
} else {
|
|
241
|
+
current[part] = {};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
current = current[part];
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function getByPath(root, path) {
|
|
249
|
+
const parts = path.split(".");
|
|
250
|
+
let current = root;
|
|
251
|
+
for (const part of parts) {
|
|
252
|
+
if (isSignal2(current)) {
|
|
253
|
+
current = current();
|
|
254
|
+
}
|
|
255
|
+
if (current[part]) {
|
|
256
|
+
current = current[part];
|
|
257
|
+
} else {
|
|
258
|
+
return void 0;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return current;
|
|
262
|
+
}
|
|
263
|
+
|
|
26
264
|
// src/utils.ts
|
|
27
265
|
import { dset } from "dset";
|
|
28
266
|
function isPromise(value) {
|
|
29
|
-
return value
|
|
267
|
+
return value instanceof Promise;
|
|
30
268
|
}
|
|
31
269
|
async function awaitReturn(val) {
|
|
32
270
|
return isPromise(val) ? await val : val;
|
|
33
271
|
}
|
|
34
|
-
function
|
|
272
|
+
function isClass2(obj) {
|
|
35
273
|
return typeof obj === "function" && obj.prototype && obj.prototype.constructor === obj;
|
|
36
274
|
}
|
|
37
275
|
function throttle(func, wait) {
|
|
@@ -58,18 +296,25 @@ function extractParams(pattern, str) {
|
|
|
58
296
|
const match = regex.exec(str);
|
|
59
297
|
if (match && match.groups) {
|
|
60
298
|
return match.groups;
|
|
299
|
+
} else if (pattern === str) {
|
|
300
|
+
return {};
|
|
61
301
|
} else {
|
|
62
302
|
return null;
|
|
63
303
|
}
|
|
64
304
|
}
|
|
65
305
|
function dremove(obj, keys) {
|
|
66
|
-
|
|
67
|
-
|
|
306
|
+
if (typeof keys === "string") {
|
|
307
|
+
keys = keys.split(".");
|
|
308
|
+
}
|
|
309
|
+
let i = 0;
|
|
310
|
+
const l = keys.length;
|
|
311
|
+
let t = obj;
|
|
312
|
+
let k;
|
|
68
313
|
while (i < l - 1) {
|
|
69
314
|
k = keys[i++];
|
|
70
315
|
if (k === "__proto__" || k === "constructor" || k === "prototype") return;
|
|
316
|
+
if (typeof t[k] !== "object" || t[k] === null) return;
|
|
71
317
|
t = t[k];
|
|
72
|
-
if (typeof t !== "object" || t === null) return;
|
|
73
318
|
}
|
|
74
319
|
k = keys[i];
|
|
75
320
|
if (t && typeof t === "object" && !(k === "__proto__" || k === "constructor" || k === "prototype")) {
|
|
@@ -81,8 +326,8 @@ function buildObject(valuesMap, allMemory) {
|
|
|
81
326
|
for (let path of valuesMap.keys()) {
|
|
82
327
|
const value = valuesMap.get(path);
|
|
83
328
|
dset(memoryObj, path, value);
|
|
84
|
-
if (
|
|
85
|
-
dremove(allMemory,
|
|
329
|
+
if (value === "$delete") {
|
|
330
|
+
dremove(allMemory, path);
|
|
86
331
|
} else {
|
|
87
332
|
dset(allMemory, path, value);
|
|
88
333
|
}
|
|
@@ -96,19 +341,81 @@ var Message = z.object({
|
|
|
96
341
|
value: z.any()
|
|
97
342
|
});
|
|
98
343
|
var Server = class {
|
|
344
|
+
/**
|
|
345
|
+
* @constructor
|
|
346
|
+
* @param {Party.Room} room - The room object representing the current game or application instance.
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* ```typescript
|
|
350
|
+
* const server = new MyServer(new ServerIo("game"));
|
|
351
|
+
* ```
|
|
352
|
+
*/
|
|
99
353
|
constructor(room) {
|
|
100
354
|
this.room = room;
|
|
101
|
-
this.
|
|
102
|
-
this.subRoom = {};
|
|
355
|
+
this.subRoom = null;
|
|
103
356
|
this.rooms = [];
|
|
104
|
-
|
|
105
|
-
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* @readonly
|
|
360
|
+
* @property {boolean} isHibernate - Indicates whether the server is in hibernate mode.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```typescript
|
|
364
|
+
* if (!server.isHibernate) {
|
|
365
|
+
* console.log("Server is active");
|
|
366
|
+
* }
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
get isHibernate() {
|
|
370
|
+
return !!this["options"]?.hibernate;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* @method onStart
|
|
374
|
+
* @async
|
|
375
|
+
* @description Initializes the server and creates the initial room if not in hibernate mode.
|
|
376
|
+
* @returns {Promise<void>}
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```typescript
|
|
380
|
+
* async function initServer() {
|
|
381
|
+
* await server.onStart();
|
|
382
|
+
* console.log("Server started");
|
|
383
|
+
* }
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
async onStart() {
|
|
387
|
+
if (!this.isHibernate) {
|
|
388
|
+
this.subRoom = await this.createRoom();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* @method createRoom
|
|
393
|
+
* @private
|
|
394
|
+
* @async
|
|
395
|
+
* @param {CreateRoomOptions} [options={}] - Options for creating the room.
|
|
396
|
+
* @returns {Promise<Object>} The created room instance.
|
|
397
|
+
* @throws {Error} If no matching room is found.
|
|
398
|
+
*
|
|
399
|
+
* @example
|
|
400
|
+
* ```typescript
|
|
401
|
+
* // This method is private and called internally
|
|
402
|
+
* async function internalCreateRoom() {
|
|
403
|
+
* const room = await this.createRoom({ getMemoryAll: true });
|
|
404
|
+
* console.log("Room created:", room);
|
|
405
|
+
* }
|
|
406
|
+
* ```
|
|
407
|
+
*/
|
|
408
|
+
async createRoom(options = {}) {
|
|
409
|
+
let instance;
|
|
410
|
+
let init = true;
|
|
411
|
+
for (let room of this.rooms) {
|
|
412
|
+
const params = extractParams(room.path, this.room.id);
|
|
106
413
|
if (params) {
|
|
107
|
-
|
|
414
|
+
instance = new room(this.room, params);
|
|
108
415
|
break;
|
|
109
416
|
}
|
|
110
417
|
}
|
|
111
|
-
if (!
|
|
418
|
+
if (!instance) {
|
|
112
419
|
throw new Error("Room not found");
|
|
113
420
|
}
|
|
114
421
|
const loadMemory = async () => {
|
|
@@ -121,75 +428,160 @@ var Server = class {
|
|
|
121
428
|
}
|
|
122
429
|
dset2(tmpObject, key, value);
|
|
123
430
|
}
|
|
124
|
-
load(
|
|
431
|
+
load(instance, tmpObject);
|
|
125
432
|
};
|
|
126
|
-
loadMemory();
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
433
|
+
await loadMemory();
|
|
434
|
+
instance.$memoryAll = {};
|
|
435
|
+
const syncCb = (values) => {
|
|
436
|
+
if (options.getMemoryAll) {
|
|
437
|
+
buildObject(values, instance.$memoryAll);
|
|
438
|
+
}
|
|
439
|
+
if (init && this.isHibernate) {
|
|
440
|
+
init = false;
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
const packet = buildObject(values, instance.$memoryAll);
|
|
444
|
+
this.room.broadcast(
|
|
445
|
+
JSON.stringify({
|
|
446
|
+
type: "sync",
|
|
447
|
+
value: packet
|
|
448
|
+
})
|
|
449
|
+
);
|
|
450
|
+
values.clear();
|
|
451
|
+
};
|
|
452
|
+
const persistCb = async (values) => {
|
|
453
|
+
for (let path of values) {
|
|
454
|
+
const _instance = path == "." ? instance : getByPath(instance, path);
|
|
455
|
+
const itemValue = createStatesSnapshot(_instance);
|
|
456
|
+
await this.room.storage.put(path, itemValue);
|
|
457
|
+
}
|
|
458
|
+
values.clear();
|
|
459
|
+
};
|
|
460
|
+
syncClass(instance, {
|
|
461
|
+
onSync: throttle(syncCb, instance["throttleSync"] ?? 500),
|
|
462
|
+
onPersist: throttle(persistCb, instance["throttleStorage"] ?? 2e3)
|
|
146
463
|
});
|
|
464
|
+
return instance;
|
|
147
465
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
466
|
+
/**
|
|
467
|
+
* @method getSubRoom
|
|
468
|
+
* @private
|
|
469
|
+
* @async
|
|
470
|
+
* @param {Object} [options={}] - Options for getting the sub-room.
|
|
471
|
+
* @returns {Promise<Object>} The sub-room instance.
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```typescript
|
|
475
|
+
* // This method is private and called internally
|
|
476
|
+
* async function internalGetSubRoom() {
|
|
477
|
+
* const subRoom = await this.getSubRoom();
|
|
478
|
+
* console.log("Sub-room retrieved:", subRoom);
|
|
479
|
+
* }
|
|
480
|
+
* ```
|
|
481
|
+
*/
|
|
482
|
+
async getSubRoom(options = {}) {
|
|
483
|
+
let subRoom;
|
|
484
|
+
if (this.isHibernate) {
|
|
485
|
+
subRoom = await this.createRoom(options);
|
|
486
|
+
} else {
|
|
487
|
+
subRoom = this.subRoom;
|
|
154
488
|
}
|
|
489
|
+
return subRoom;
|
|
155
490
|
}
|
|
156
|
-
|
|
157
|
-
|
|
491
|
+
/**
|
|
492
|
+
* @method getUsersProperty
|
|
493
|
+
* @private
|
|
494
|
+
* @param {Object} subRoom - The sub-room instance.
|
|
495
|
+
* @returns {Object|null} The users property of the sub-room, or null if not found.
|
|
496
|
+
*
|
|
497
|
+
* @example
|
|
498
|
+
* ```typescript
|
|
499
|
+
* // This method is private and called internally
|
|
500
|
+
* function internalGetUsers(subRoom) {
|
|
501
|
+
* const users = this.getUsersProperty(subRoom);
|
|
502
|
+
* console.log("Users:", users);
|
|
503
|
+
* }
|
|
504
|
+
* ```
|
|
505
|
+
*/
|
|
506
|
+
getUsersProperty(subRoom) {
|
|
507
|
+
const meta = subRoom.constructor["_propertyMetadata"];
|
|
158
508
|
const propId = meta?.get("users");
|
|
159
509
|
if (propId) {
|
|
160
|
-
return
|
|
510
|
+
return subRoom[propId];
|
|
161
511
|
}
|
|
162
512
|
return null;
|
|
163
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* @method onConnect
|
|
516
|
+
* @async
|
|
517
|
+
* @param {Party.Connection} conn - The connection object for the new user.
|
|
518
|
+
* @param {Party.ConnectionContext} ctx - The context of the connection.
|
|
519
|
+
* @description Handles a new user connection, creates a user object, and sends initial sync data.
|
|
520
|
+
* @returns {Promise<void>}
|
|
521
|
+
*
|
|
522
|
+
* @example
|
|
523
|
+
* ```typescript
|
|
524
|
+
* server.onConnect = async (conn, ctx) => {
|
|
525
|
+
* await server.onConnect(conn, ctx);
|
|
526
|
+
* console.log("New user connected:", conn.id);
|
|
527
|
+
* };
|
|
528
|
+
* ```
|
|
529
|
+
*/
|
|
164
530
|
async onConnect(conn, ctx) {
|
|
165
|
-
const
|
|
531
|
+
const subRoom = await this.getSubRoom({
|
|
532
|
+
getMemoryAll: true
|
|
533
|
+
});
|
|
534
|
+
const publicId = generateShortUUID();
|
|
166
535
|
let user = null;
|
|
167
|
-
const signal = this.getUsersProperty();
|
|
536
|
+
const signal = this.getUsersProperty(subRoom);
|
|
168
537
|
if (signal) {
|
|
169
538
|
const { classType } = signal.options;
|
|
170
|
-
user =
|
|
539
|
+
user = isClass2(classType) ? new classType() : classType(conn, ctx);
|
|
171
540
|
signal()[publicId] = user;
|
|
172
541
|
}
|
|
173
|
-
await awaitReturn(
|
|
542
|
+
await awaitReturn(subRoom["onJoin"]?.(user, conn, ctx));
|
|
174
543
|
conn.setState({ publicId });
|
|
175
544
|
conn.send(
|
|
176
545
|
JSON.stringify({
|
|
177
546
|
type: "sync",
|
|
178
547
|
value: {
|
|
179
548
|
pId: publicId,
|
|
180
|
-
...
|
|
549
|
+
...subRoom.$memoryAll
|
|
181
550
|
}
|
|
182
551
|
})
|
|
183
552
|
);
|
|
184
553
|
}
|
|
554
|
+
/**
|
|
555
|
+
* @method onMessage
|
|
556
|
+
* @async
|
|
557
|
+
* @param {string} message - The message received from a user.
|
|
558
|
+
* @param {Party.Connection} sender - The connection object of the sender.
|
|
559
|
+
* @description Processes incoming messages and triggers corresponding actions in the sub-room.
|
|
560
|
+
* @returns {Promise<void>}
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* ```typescript
|
|
564
|
+
* server.onMessage = async (message, sender) => {
|
|
565
|
+
* await server.onMessage(message, sender);
|
|
566
|
+
* console.log("Message processed from:", sender.id);
|
|
567
|
+
* };
|
|
568
|
+
* ```
|
|
569
|
+
*/
|
|
185
570
|
async onMessage(message, sender) {
|
|
186
|
-
|
|
187
|
-
|
|
571
|
+
let json;
|
|
572
|
+
try {
|
|
573
|
+
json = JSON.parse(message);
|
|
574
|
+
} catch (e) {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const result = Message.safeParse(json);
|
|
188
578
|
if (!result.success) {
|
|
189
579
|
return;
|
|
190
580
|
}
|
|
581
|
+
const subRoom = await this.getSubRoom();
|
|
582
|
+
const actions = subRoom.constructor["_actionMetadata"];
|
|
191
583
|
if (actions) {
|
|
192
|
-
const signal = this.getUsersProperty();
|
|
584
|
+
const signal = this.getUsersProperty(subRoom);
|
|
193
585
|
const { publicId } = sender.state;
|
|
194
586
|
const user = signal?.()[publicId];
|
|
195
587
|
const actionName = actions.get(result.data.action);
|
|
@@ -203,24 +595,43 @@ var Server = class {
|
|
|
203
595
|
}
|
|
204
596
|
}
|
|
205
597
|
await awaitReturn(
|
|
206
|
-
|
|
598
|
+
subRoom[actionName.key](user, result.data.value, sender)
|
|
207
599
|
);
|
|
208
600
|
}
|
|
209
601
|
}
|
|
210
602
|
}
|
|
603
|
+
/**
|
|
604
|
+
* @method onClose
|
|
605
|
+
* @async
|
|
606
|
+
* @param {Party.Connection} conn - The connection object of the disconnecting user.
|
|
607
|
+
* @description Handles user disconnection, removing them from the room and triggering the onLeave event.
|
|
608
|
+
* @returns {Promise<void>}
|
|
609
|
+
*
|
|
610
|
+
* @example
|
|
611
|
+
* ```typescript
|
|
612
|
+
* server.onClose = async (conn) => {
|
|
613
|
+
* await server.onClose(conn);
|
|
614
|
+
* console.log("User disconnected:", conn.id);
|
|
615
|
+
* };
|
|
616
|
+
* ```
|
|
617
|
+
*/
|
|
211
618
|
async onClose(conn) {
|
|
212
|
-
const
|
|
619
|
+
const subRoom = await this.getSubRoom();
|
|
620
|
+
const signal = this.getUsersProperty(subRoom);
|
|
213
621
|
const { publicId } = conn.state;
|
|
214
622
|
const user = signal?.()[publicId];
|
|
215
|
-
await awaitReturn(
|
|
623
|
+
await awaitReturn(subRoom["onLeave"]?.(user, conn));
|
|
216
624
|
if (signal) {
|
|
217
625
|
delete signal()[publicId];
|
|
218
626
|
}
|
|
219
627
|
}
|
|
220
628
|
};
|
|
221
629
|
export {
|
|
630
|
+
Action,
|
|
631
|
+
ClientIo,
|
|
632
|
+
MockConnection,
|
|
222
633
|
Room,
|
|
223
634
|
Server,
|
|
224
|
-
|
|
635
|
+
ServerIo
|
|
225
636
|
};
|
|
226
637
|
//# sourceMappingURL=index.js.map
|