@colyseus/core 0.17.52 → 0.17.54
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/build/Room.cjs +1 -1
- package/build/Room.cjs.map +2 -2
- package/build/Room.mjs +1 -1
- package/build/Room.mjs.map +2 -2
- package/build/Transport.cjs.map +2 -2
- package/build/Transport.d.ts +11 -0
- package/build/Transport.mjs.map +2 -2
- package/build/matchmaker/LocalDriver/Query.cjs +5 -5
- package/build/matchmaker/LocalDriver/Query.cjs.map +2 -2
- package/build/matchmaker/LocalDriver/Query.mjs +5 -5
- package/build/matchmaker/LocalDriver/Query.mjs.map +2 -2
- package/build/matchmaker/RegisteredHandler.cjs +1 -1
- package/build/matchmaker/RegisteredHandler.cjs.map +2 -2
- package/build/matchmaker/RegisteredHandler.mjs +1 -1
- package/build/matchmaker/RegisteredHandler.mjs.map +2 -2
- package/build/serializer/SchemaSerializer.cjs +25 -9
- package/build/serializer/SchemaSerializer.cjs.map +2 -2
- package/build/serializer/SchemaSerializer.d.ts +10 -0
- package/build/serializer/SchemaSerializer.mjs +25 -9
- package/build/serializer/SchemaSerializer.mjs.map +2 -2
- package/build/utils/Utils.cjs +1 -1
- package/build/utils/Utils.cjs.map +2 -2
- package/build/utils/Utils.mjs +1 -1
- package/build/utils/Utils.mjs.map +2 -2
- package/package.json +8 -8
- package/src/Room.ts +2 -1
- package/src/Transport.ts +11 -0
- package/src/matchmaker/LocalDriver/Query.ts +5 -5
- package/src/matchmaker/RegisteredHandler.ts +1 -1
- package/src/serializer/SchemaSerializer.ts +27 -9
- package/src/utils/Utils.ts +2 -1
|
@@ -39,7 +39,7 @@ var Query = class {
|
|
|
39
39
|
return rooms.sort((room1, room2) => {
|
|
40
40
|
for (const field in this.sortOptions) {
|
|
41
41
|
const direction = this.sortOptions[field];
|
|
42
|
-
if (
|
|
42
|
+
if (Object.hasOwn(room1, field)) {
|
|
43
43
|
if (direction === 1 || direction === "asc" || direction === "ascending") {
|
|
44
44
|
if (room1[field] > room2[field]) {
|
|
45
45
|
return 1;
|
|
@@ -55,7 +55,7 @@ var Query = class {
|
|
|
55
55
|
return 1;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
} else if (room1.metadata
|
|
58
|
+
} else if (room1.metadata && Object.hasOwn(room1.metadata, field)) {
|
|
59
59
|
if (direction === 1 || direction === "asc" || direction === "ascending") {
|
|
60
60
|
if (room1.metadata[field] > room2.metadata[field]) {
|
|
61
61
|
return 1;
|
|
@@ -79,12 +79,12 @@ var Query = class {
|
|
|
79
79
|
applyFilter(rooms, conditions) {
|
|
80
80
|
return rooms.filter(((room) => {
|
|
81
81
|
for (const field in conditions) {
|
|
82
|
-
if (
|
|
83
|
-
if (
|
|
82
|
+
if (Object.hasOwn(conditions, field)) {
|
|
83
|
+
if (Object.hasOwn(room, field)) {
|
|
84
84
|
if (room[field] !== conditions[field]) {
|
|
85
85
|
return false;
|
|
86
86
|
}
|
|
87
|
-
} else if (room.metadata
|
|
87
|
+
} else if (room.metadata && Object.hasOwn(room.metadata, field)) {
|
|
88
88
|
if (room.metadata[field] !== conditions[field]) {
|
|
89
89
|
return false;
|
|
90
90
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/matchmaker/LocalDriver/Query.ts"],
|
|
4
|
-
"sourcesContent": ["import type { IRoomCache, SortOptions } from '../driver.ts';\n\nexport class Query<T extends IRoomCache> {\n private $rooms: T[];\n private conditions: any;\n private sortOptions?: SortOptions;\n\n constructor(rooms: any[], conditions) {\n this.$rooms = rooms.slice(0);\n this.conditions = conditions;\n }\n\n public sort(options: SortOptions) {\n // Store sort options instead of sorting immediately\n // This allows filtering first, then sorting fewer items\n this.sortOptions = options;\n return this;\n }\n\n private applySort(rooms: T[]): T[] {\n if (!this.sortOptions) {\n return rooms;\n }\n\n return rooms.sort((room1: T, room2: T) => {\n for (const field in this.sortOptions) {\n const direction = this.sortOptions[field];\n if (
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEO,IAAM,QAAN,MAAkC;AAAA,EAKvC,YAAY,OAAc,YAAY;AACpC,SAAK,SAAS,MAAM,MAAM,CAAC;AAC3B,SAAK,aAAa;AAAA,EACpB;AAAA,EAEO,KAAK,SAAsB;AAGhC,SAAK,cAAc;AACnB,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,OAAiB;AACjC,QAAI,CAAC,KAAK,aAAa;AACrB,aAAO;AAAA,IACT;AAEA,WAAO,MAAM,KAAK,CAAC,OAAU,UAAa;AACxC,iBAAW,SAAS,KAAK,aAAa;AACpC,cAAM,YAAY,KAAK,YAAY,KAAK;AACxC,YAAI,
|
|
4
|
+
"sourcesContent": ["import type { IRoomCache, SortOptions } from '../driver.ts';\n\nexport class Query<T extends IRoomCache> {\n private $rooms: T[];\n private conditions: any;\n private sortOptions?: SortOptions;\n\n constructor(rooms: any[], conditions) {\n this.$rooms = rooms.slice(0);\n this.conditions = conditions;\n }\n\n public sort(options: SortOptions) {\n // Store sort options instead of sorting immediately\n // This allows filtering first, then sorting fewer items\n this.sortOptions = options;\n return this;\n }\n\n private applySort(rooms: T[]): T[] {\n if (!this.sortOptions) {\n return rooms;\n }\n\n return rooms.sort((room1: T, room2: T) => {\n for (const field in this.sortOptions) {\n const direction = this.sortOptions[field];\n if (Object.hasOwn(room1, field)) {\n /**\n * IRoomCache field\n */\n if (direction === 1 || direction === 'asc' || direction === 'ascending') {\n if (room1[field] > room2[field]) { return 1; }\n if (room1[field] < room2[field]) { return -1; }\n\n } else {\n if (room1[field] > room2[field]) { return -1; }\n if (room1[field] < room2[field]) { return 1; }\n }\n } else if (room1.metadata && Object.hasOwn(room1.metadata, field)) {\n /**\n * metadata field\n */\n if (direction === 1 || direction === 'asc' || direction === 'ascending') {\n if (room1.metadata[field] > room2.metadata[field]) { return 1; }\n if (room1.metadata[field] < room2.metadata[field]) { return -1; }\n } else {\n if (room1.metadata[field] > room2.metadata[field]) { return -1; }\n if (room1.metadata[field] < room2.metadata[field]) { return 1; }\n }\n }\n }\n return 0;\n });\n }\n\n private applyFilter(rooms: T[], conditions: any): T[] {\n return rooms.filter(((room) => {\n for (const field in conditions) {\n if (Object.hasOwn(conditions, field)) {\n // Check if field exists in room (IRoomCache base fields)\n if (Object.hasOwn(room, field)) {\n if (room[field] !== conditions[field]) {\n return false;\n }\n } else if (room.metadata && Object.hasOwn(room.metadata, field)) {\n // Check if field exists in metadata\n if (room.metadata[field] !== conditions[field]) {\n return false;\n }\n } else {\n // Field doesn't exist in room or metadata\n return false;\n }\n }\n }\n return true;\n }));\n }\n\n public filter(conditions: any) {\n // Filter first to reduce the number of items to sort\n const filtered = this.applyFilter(this.$rooms, conditions);\n return this.applySort(filtered);\n }\n\n public then(resolve, reject) {\n // Filter first to reduce the number of items to sort\n const filtered = this.applyFilter(this.$rooms, this.conditions);\n const sorted = this.applySort(filtered);\n const result: any = sorted[0];\n return resolve(result);\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEO,IAAM,QAAN,MAAkC;AAAA,EAKvC,YAAY,OAAc,YAAY;AACpC,SAAK,SAAS,MAAM,MAAM,CAAC;AAC3B,SAAK,aAAa;AAAA,EACpB;AAAA,EAEO,KAAK,SAAsB;AAGhC,SAAK,cAAc;AACnB,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,OAAiB;AACjC,QAAI,CAAC,KAAK,aAAa;AACrB,aAAO;AAAA,IACT;AAEA,WAAO,MAAM,KAAK,CAAC,OAAU,UAAa;AACxC,iBAAW,SAAS,KAAK,aAAa;AACpC,cAAM,YAAY,KAAK,YAAY,KAAK;AACxC,YAAI,OAAO,OAAO,OAAO,KAAK,GAAG;AAI/B,cAAI,cAAc,KAAK,cAAc,SAAS,cAAc,aAAa;AACvE,gBAAI,MAAM,KAAK,IAAI,MAAM,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAG;AAC7C,gBAAI,MAAM,KAAK,IAAI,MAAM,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAI;AAAA,UAEhD,OAAO;AACL,gBAAI,MAAM,KAAK,IAAI,MAAM,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAI;AAC9C,gBAAI,MAAM,KAAK,IAAI,MAAM,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAG;AAAA,UAC/C;AAAA,QACF,WAAW,MAAM,YAAY,OAAO,OAAO,MAAM,UAAU,KAAK,GAAG;AAIjE,cAAI,cAAc,KAAK,cAAc,SAAS,cAAc,aAAa;AACvE,gBAAI,MAAM,SAAS,KAAK,IAAI,MAAM,SAAS,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAG;AAC/D,gBAAI,MAAM,SAAS,KAAK,IAAI,MAAM,SAAS,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAI;AAAA,UAClE,OAAO;AACL,gBAAI,MAAM,SAAS,KAAK,IAAI,MAAM,SAAS,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAI;AAChE,gBAAI,MAAM,SAAS,KAAK,IAAI,MAAM,SAAS,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAG;AAAA,UACjE;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEQ,YAAY,OAAY,YAAsB;AACpD,WAAO,MAAM,QAAQ,CAAC,SAAS;AAC7B,iBAAW,SAAS,YAAY;AAC9B,YAAI,OAAO,OAAO,YAAY,KAAK,GAAG;AAEpC,cAAI,OAAO,OAAO,MAAM,KAAK,GAAG;AAC9B,gBAAI,KAAK,KAAK,MAAM,WAAW,KAAK,GAAG;AACrC,qBAAO;AAAA,YACT;AAAA,UACF,WAAW,KAAK,YAAY,OAAO,OAAO,KAAK,UAAU,KAAK,GAAG;AAE/D,gBAAI,KAAK,SAAS,KAAK,MAAM,WAAW,KAAK,GAAG;AAC9C,qBAAO;AAAA,YACT;AAAA,UACF,OAAO;AAEL,mBAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT,EAAE;AAAA,EACJ;AAAA,EAEO,OAAO,YAAiB;AAE7B,UAAM,WAAW,KAAK,YAAY,KAAK,QAAQ,UAAU;AACzD,WAAO,KAAK,UAAU,QAAQ;AAAA,EAChC;AAAA,EAEO,KAAK,SAAS,QAAQ;AAE3B,UAAM,WAAW,KAAK,YAAY,KAAK,QAAQ,KAAK,UAAU;AAC9D,UAAM,SAAS,KAAK,UAAU,QAAQ;AACtC,UAAM,SAAc,OAAO,CAAC;AAC5B,WAAO,QAAQ,MAAM;AAAA,EACvB;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -15,7 +15,7 @@ var Query = class {
|
|
|
15
15
|
return rooms.sort((room1, room2) => {
|
|
16
16
|
for (const field in this.sortOptions) {
|
|
17
17
|
const direction = this.sortOptions[field];
|
|
18
|
-
if (
|
|
18
|
+
if (Object.hasOwn(room1, field)) {
|
|
19
19
|
if (direction === 1 || direction === "asc" || direction === "ascending") {
|
|
20
20
|
if (room1[field] > room2[field]) {
|
|
21
21
|
return 1;
|
|
@@ -31,7 +31,7 @@ var Query = class {
|
|
|
31
31
|
return 1;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
} else if (room1.metadata
|
|
34
|
+
} else if (room1.metadata && Object.hasOwn(room1.metadata, field)) {
|
|
35
35
|
if (direction === 1 || direction === "asc" || direction === "ascending") {
|
|
36
36
|
if (room1.metadata[field] > room2.metadata[field]) {
|
|
37
37
|
return 1;
|
|
@@ -55,12 +55,12 @@ var Query = class {
|
|
|
55
55
|
applyFilter(rooms, conditions) {
|
|
56
56
|
return rooms.filter(((room) => {
|
|
57
57
|
for (const field in conditions) {
|
|
58
|
-
if (
|
|
59
|
-
if (
|
|
58
|
+
if (Object.hasOwn(conditions, field)) {
|
|
59
|
+
if (Object.hasOwn(room, field)) {
|
|
60
60
|
if (room[field] !== conditions[field]) {
|
|
61
61
|
return false;
|
|
62
62
|
}
|
|
63
|
-
} else if (room.metadata
|
|
63
|
+
} else if (room.metadata && Object.hasOwn(room.metadata, field)) {
|
|
64
64
|
if (room.metadata[field] !== conditions[field]) {
|
|
65
65
|
return false;
|
|
66
66
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/matchmaker/LocalDriver/Query.ts"],
|
|
4
|
-
"sourcesContent": ["import type { IRoomCache, SortOptions } from '../driver.ts';\n\nexport class Query<T extends IRoomCache> {\n private $rooms: T[];\n private conditions: any;\n private sortOptions?: SortOptions;\n\n constructor(rooms: any[], conditions) {\n this.$rooms = rooms.slice(0);\n this.conditions = conditions;\n }\n\n public sort(options: SortOptions) {\n // Store sort options instead of sorting immediately\n // This allows filtering first, then sorting fewer items\n this.sortOptions = options;\n return this;\n }\n\n private applySort(rooms: T[]): T[] {\n if (!this.sortOptions) {\n return rooms;\n }\n\n return rooms.sort((room1: T, room2: T) => {\n for (const field in this.sortOptions) {\n const direction = this.sortOptions[field];\n if (
|
|
5
|
-
"mappings": ";AAEO,IAAM,QAAN,MAAkC;AAAA,EAKvC,YAAY,OAAc,YAAY;AACpC,SAAK,SAAS,MAAM,MAAM,CAAC;AAC3B,SAAK,aAAa;AAAA,EACpB;AAAA,EAEO,KAAK,SAAsB;AAGhC,SAAK,cAAc;AACnB,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,OAAiB;AACjC,QAAI,CAAC,KAAK,aAAa;AACrB,aAAO;AAAA,IACT;AAEA,WAAO,MAAM,KAAK,CAAC,OAAU,UAAa;AACxC,iBAAW,SAAS,KAAK,aAAa;AACpC,cAAM,YAAY,KAAK,YAAY,KAAK;AACxC,YAAI,
|
|
4
|
+
"sourcesContent": ["import type { IRoomCache, SortOptions } from '../driver.ts';\n\nexport class Query<T extends IRoomCache> {\n private $rooms: T[];\n private conditions: any;\n private sortOptions?: SortOptions;\n\n constructor(rooms: any[], conditions) {\n this.$rooms = rooms.slice(0);\n this.conditions = conditions;\n }\n\n public sort(options: SortOptions) {\n // Store sort options instead of sorting immediately\n // This allows filtering first, then sorting fewer items\n this.sortOptions = options;\n return this;\n }\n\n private applySort(rooms: T[]): T[] {\n if (!this.sortOptions) {\n return rooms;\n }\n\n return rooms.sort((room1: T, room2: T) => {\n for (const field in this.sortOptions) {\n const direction = this.sortOptions[field];\n if (Object.hasOwn(room1, field)) {\n /**\n * IRoomCache field\n */\n if (direction === 1 || direction === 'asc' || direction === 'ascending') {\n if (room1[field] > room2[field]) { return 1; }\n if (room1[field] < room2[field]) { return -1; }\n\n } else {\n if (room1[field] > room2[field]) { return -1; }\n if (room1[field] < room2[field]) { return 1; }\n }\n } else if (room1.metadata && Object.hasOwn(room1.metadata, field)) {\n /**\n * metadata field\n */\n if (direction === 1 || direction === 'asc' || direction === 'ascending') {\n if (room1.metadata[field] > room2.metadata[field]) { return 1; }\n if (room1.metadata[field] < room2.metadata[field]) { return -1; }\n } else {\n if (room1.metadata[field] > room2.metadata[field]) { return -1; }\n if (room1.metadata[field] < room2.metadata[field]) { return 1; }\n }\n }\n }\n return 0;\n });\n }\n\n private applyFilter(rooms: T[], conditions: any): T[] {\n return rooms.filter(((room) => {\n for (const field in conditions) {\n if (Object.hasOwn(conditions, field)) {\n // Check if field exists in room (IRoomCache base fields)\n if (Object.hasOwn(room, field)) {\n if (room[field] !== conditions[field]) {\n return false;\n }\n } else if (room.metadata && Object.hasOwn(room.metadata, field)) {\n // Check if field exists in metadata\n if (room.metadata[field] !== conditions[field]) {\n return false;\n }\n } else {\n // Field doesn't exist in room or metadata\n return false;\n }\n }\n }\n return true;\n }));\n }\n\n public filter(conditions: any) {\n // Filter first to reduce the number of items to sort\n const filtered = this.applyFilter(this.$rooms, conditions);\n return this.applySort(filtered);\n }\n\n public then(resolve, reject) {\n // Filter first to reduce the number of items to sort\n const filtered = this.applyFilter(this.$rooms, this.conditions);\n const sorted = this.applySort(filtered);\n const result: any = sorted[0];\n return resolve(result);\n }\n}\n"],
|
|
5
|
+
"mappings": ";AAEO,IAAM,QAAN,MAAkC;AAAA,EAKvC,YAAY,OAAc,YAAY;AACpC,SAAK,SAAS,MAAM,MAAM,CAAC;AAC3B,SAAK,aAAa;AAAA,EACpB;AAAA,EAEO,KAAK,SAAsB;AAGhC,SAAK,cAAc;AACnB,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,OAAiB;AACjC,QAAI,CAAC,KAAK,aAAa;AACrB,aAAO;AAAA,IACT;AAEA,WAAO,MAAM,KAAK,CAAC,OAAU,UAAa;AACxC,iBAAW,SAAS,KAAK,aAAa;AACpC,cAAM,YAAY,KAAK,YAAY,KAAK;AACxC,YAAI,OAAO,OAAO,OAAO,KAAK,GAAG;AAI/B,cAAI,cAAc,KAAK,cAAc,SAAS,cAAc,aAAa;AACvE,gBAAI,MAAM,KAAK,IAAI,MAAM,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAG;AAC7C,gBAAI,MAAM,KAAK,IAAI,MAAM,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAI;AAAA,UAEhD,OAAO;AACL,gBAAI,MAAM,KAAK,IAAI,MAAM,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAI;AAC9C,gBAAI,MAAM,KAAK,IAAI,MAAM,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAG;AAAA,UAC/C;AAAA,QACF,WAAW,MAAM,YAAY,OAAO,OAAO,MAAM,UAAU,KAAK,GAAG;AAIjE,cAAI,cAAc,KAAK,cAAc,SAAS,cAAc,aAAa;AACvE,gBAAI,MAAM,SAAS,KAAK,IAAI,MAAM,SAAS,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAG;AAC/D,gBAAI,MAAM,SAAS,KAAK,IAAI,MAAM,SAAS,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAI;AAAA,UAClE,OAAO;AACL,gBAAI,MAAM,SAAS,KAAK,IAAI,MAAM,SAAS,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAI;AAChE,gBAAI,MAAM,SAAS,KAAK,IAAI,MAAM,SAAS,KAAK,GAAG;AAAE,qBAAO;AAAA,YAAG;AAAA,UACjE;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEQ,YAAY,OAAY,YAAsB;AACpD,WAAO,MAAM,QAAQ,CAAC,SAAS;AAC7B,iBAAW,SAAS,YAAY;AAC9B,YAAI,OAAO,OAAO,YAAY,KAAK,GAAG;AAEpC,cAAI,OAAO,OAAO,MAAM,KAAK,GAAG;AAC9B,gBAAI,KAAK,KAAK,MAAM,WAAW,KAAK,GAAG;AACrC,qBAAO;AAAA,YACT;AAAA,UACF,WAAW,KAAK,YAAY,OAAO,OAAO,KAAK,UAAU,KAAK,GAAG;AAE/D,gBAAI,KAAK,SAAS,KAAK,MAAM,WAAW,KAAK,GAAG;AAC9C,qBAAO;AAAA,YACT;AAAA,UACF,OAAO;AAEL,mBAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT,EAAE;AAAA,EACJ;AAAA,EAEO,OAAO,YAAiB;AAE7B,UAAM,WAAW,KAAK,YAAY,KAAK,QAAQ,UAAU;AACzD,WAAO,KAAK,UAAU,QAAQ;AAAA,EAChC;AAAA,EAEO,KAAK,SAAS,QAAQ;AAE3B,UAAM,WAAW,KAAK,YAAY,KAAK,QAAQ,KAAK,UAAU;AAC9D,UAAM,SAAS,KAAK,UAAU,QAAQ;AACtC,UAAM,SAAc,OAAO,CAAC;AAC5B,WAAO,QAAQ,MAAM;AAAA,EACvB;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -123,7 +123,7 @@ var RegisteredHandler = class extends import_events.EventEmitter {
|
|
|
123
123
|
getFilterOptions(options) {
|
|
124
124
|
return this.filterOptions.reduce((prev, curr, i, arr) => {
|
|
125
125
|
const field = String(arr[i]);
|
|
126
|
-
if (
|
|
126
|
+
if (Object.hasOwn(options, field)) {
|
|
127
127
|
if (INVALID_OPTION_KEYS.indexOf(field) !== -1) {
|
|
128
128
|
import_Logger.logger.warn(`option "${field}" has internal usage and is going to be ignored.`);
|
|
129
129
|
} else {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/matchmaker/RegisteredHandler.ts"],
|
|
4
|
-
"sourcesContent": ["import { EventEmitter } from 'events';\nimport { logger } from '../Logger.ts';\nimport { Room } from './../Room.ts';\nimport { updateLobby } from './Lobby.ts';\n\nimport type { IRoomCache, SortOptions, IRoomCacheFilterByKeys, IRoomCacheSortByKeys, ExtractRoomCacheMetadata } from './driver.ts';\nimport type { Client } from '../Transport.ts';\nimport type { Type } from \"../utils/Utils.ts\";\n\nexport const INVALID_OPTION_KEYS: Array<keyof IRoomCache> = [\n 'clients',\n 'locked',\n 'private',\n // 'maxClients', - maxClients can be useful as filter options\n 'metadata',\n 'name',\n 'processId',\n 'roomId',\n];\n\n/**\n * Type for filterBy that supports both onCreate options and metadata fields\n */\ntype FilterByKeys<RoomType extends Room> =\n | IRoomCacheFilterByKeys\n | (ExtractRoomCacheMetadata<RoomType> extends object\n ? keyof ExtractRoomCacheMetadata<RoomType> & string\n : never)\n\n/**\n * Type for sortBy that supports room cache fields and metadata fields\n */\ntype SortByKeys<RoomType extends Room> =\n | IRoomCacheSortByKeys\n | (ExtractRoomCacheMetadata<RoomType> extends object\n ? keyof ExtractRoomCacheMetadata<RoomType> & string\n : never);\n\nexport interface RegisteredHandlerEvents<RoomType extends Room = any> {\n create: [room: RoomType];\n lock: [room: RoomType];\n unlock: [room: RoomType];\n join: [room: RoomType, client: Client];\n leave: [room: RoomType, client: Client, willDispose: boolean];\n dispose: [room: RoomType];\n 'visibility-change': [room: RoomType, isVisible: boolean];\n 'metadata-change': [room: RoomType];\n}\n\nexport class RegisteredHandler<\n RoomType extends Room = any\n> extends EventEmitter<RegisteredHandlerEvents<RoomType>> {\n '~room': RoomType;\n\n public klass: Type<RoomType>;\n public options: any;\n\n public name: string;\n public filterOptions: Array<FilterByKeys<RoomType>> = [];\n public sortOptions?: SortOptions;\n\n public realtimeListingEnabled: boolean = false;\n\n constructor(klass: Type<RoomType>, options?: any) {\n super();\n\n this.klass = klass;\n this.options = options;\n\n if (typeof(klass) !== 'function') {\n logger.debug('You are likely not importing your room class correctly.');\n throw new Error(`class is expected but ${typeof(klass)} was provided.`);\n }\n }\n\n public enableRealtimeListing() {\n this.realtimeListingEnabled = true;\n this.on('create', (room) => updateLobby(room));\n this.on('lock', (room) => updateLobby(room));\n this.on('unlock', (room) => updateLobby(room));\n this.on('join', (room) => updateLobby(room));\n this.on('leave', (room, _, willDispose) => {\n if (!willDispose) {\n updateLobby(room);\n }\n });\n this.on('visibility-change', (room, isVisible) => updateLobby(room, isVisible));\n this.on('metadata-change', (room) => updateLobby(room));\n this.on('dispose', (room) => updateLobby(room, true));\n return this;\n }\n\n /**\n * Define which fields should be used for filtering rooms.\n * Supports both onCreate options and metadata fields using dot notation.\n *\n * @example\n * // Filter by IRoomCache fields\n * .filterBy(['maxClients'])\n *\n * @example\n * // Filter by metadata fields\n * .filterBy(['difficulty', 'metadata.region'])\n *\n * @example\n * // Mix both\n * .filterBy(['mode', 'difficulty', 'maxClients'])\n */\n public filterBy<T extends FilterByKeys<RoomType>>(\n options: T[]\n ) {\n this.filterOptions = options;\n return this;\n }\n\n /**\n * Define how rooms should be sorted when querying.\n * Supports both room cache fields and metadata fields using dot notation.\n *\n * @example\n * // Sort by number of clients (descending)\n * .sortBy({ clients: -1 })\n *\n * @example\n * // Sort by metadata field\n * .sortBy({ 'metadata.rating': -1 })\n *\n * @example\n * // Multiple sort criteria\n * .sortBy({ 'metadata.skillLevel': 1, clients: -1 })\n */\n public sortBy<T extends SortByKeys<RoomType>>(\n options: { [K in T]: SortOptions[string] }\n ): this {\n this.sortOptions = options as unknown as SortOptions;\n return this;\n }\n\n public getMetadataFromOptions(options: any) {\n const metadata = this.getFilterOptions(options);\n\n if (this.sortOptions) {\n for (const field in this.sortOptions) {\n if (field in options && !(field in metadata)) {\n metadata[field] = options[field];\n }\n }\n }\n\n return Object.keys(metadata).length > 0 ? { metadata } : {};\n }\n\n /**\n * Extract filter options from client options.\n */\n public getFilterOptions(options: any) {\n return this.filterOptions.reduce((prev, curr, i, arr) => {\n const field = String(arr[i]);\n\n // Handle regular (non-metadata) fields\n if (
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA6B;AAC7B,oBAAuB;AACvB,kBAAqB;AACrB,mBAA4B;AAMrB,IAAM,sBAA+C;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA+BO,IAAM,oBAAN,cAEG,2BAAgD;AAAA,EAYxD,YAAY,OAAuB,SAAe;AAChD,UAAM;AANR,SAAO,gBAA+C,CAAC;AAGvD,SAAO,yBAAkC;AAKvC,SAAK,QAAQ;AACb,SAAK,UAAU;AAEf,QAAI,OAAO,UAAW,YAAY;AAChC,2BAAO,MAAM,yDAAyD;AACtE,YAAM,IAAI,MAAM,yBAAyB,OAAO,KAAM,gBAAgB;AAAA,IACxE;AAAA,EACF;AAAA,EAEO,wBAAwB;AAC7B,SAAK,yBAAyB;AAC9B,SAAK,GAAG,UAAU,CAAC,aAAS,0BAAY,IAAI,CAAC;AAC7C,SAAK,GAAG,QAAQ,CAAC,aAAS,0BAAY,IAAI,CAAC;AAC3C,SAAK,GAAG,UAAU,CAAC,aAAS,0BAAY,IAAI,CAAC;AAC7C,SAAK,GAAG,QAAQ,CAAC,aAAS,0BAAY,IAAI,CAAC;AAC3C,SAAK,GAAG,SAAS,CAAC,MAAM,GAAG,gBAAgB;AACzC,UAAI,CAAC,aAAa;AAChB,sCAAY,IAAI;AAAA,MAClB;AAAA,IACF,CAAC;AACD,SAAK,GAAG,qBAAqB,CAAC,MAAM,kBAAc,0BAAY,MAAM,SAAS,CAAC;AAC9E,SAAK,GAAG,mBAAmB,CAAC,aAAS,0BAAY,IAAI,CAAC;AACtD,SAAK,GAAG,WAAW,CAAC,aAAS,0BAAY,MAAM,IAAI,CAAC;AACpD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBO,SACL,SACA;AACA,SAAK,gBAAgB;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBO,OACL,SACM;AACN,SAAK,cAAc;AACnB,WAAO;AAAA,EACT;AAAA,EAEO,uBAAuB,SAAc;AAC1C,UAAM,WAAW,KAAK,iBAAiB,OAAO;AAE9C,QAAI,KAAK,aAAa;AACpB,iBAAW,SAAS,KAAK,aAAa;AACpC,YAAI,SAAS,WAAW,EAAE,SAAS,WAAW;AAC5C,mBAAS,KAAK,IAAI,QAAQ,KAAK;AAAA,QACjC;AAAA,MACF;AAAA,IACF;AAEA,WAAO,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA,EAKO,iBAAiB,SAAc;AACpC,WAAO,KAAK,cAAc,OAAO,CAAC,MAAM,MAAM,GAAG,QAAQ;AACvD,YAAM,QAAQ,OAAO,IAAI,CAAC,CAAC;AAG3B,UAAI,
|
|
4
|
+
"sourcesContent": ["import { EventEmitter } from 'events';\nimport { logger } from '../Logger.ts';\nimport { Room } from './../Room.ts';\nimport { updateLobby } from './Lobby.ts';\n\nimport type { IRoomCache, SortOptions, IRoomCacheFilterByKeys, IRoomCacheSortByKeys, ExtractRoomCacheMetadata } from './driver.ts';\nimport type { Client } from '../Transport.ts';\nimport type { Type } from \"../utils/Utils.ts\";\n\nexport const INVALID_OPTION_KEYS: Array<keyof IRoomCache> = [\n 'clients',\n 'locked',\n 'private',\n // 'maxClients', - maxClients can be useful as filter options\n 'metadata',\n 'name',\n 'processId',\n 'roomId',\n];\n\n/**\n * Type for filterBy that supports both onCreate options and metadata fields\n */\ntype FilterByKeys<RoomType extends Room> =\n | IRoomCacheFilterByKeys\n | (ExtractRoomCacheMetadata<RoomType> extends object\n ? keyof ExtractRoomCacheMetadata<RoomType> & string\n : never)\n\n/**\n * Type for sortBy that supports room cache fields and metadata fields\n */\ntype SortByKeys<RoomType extends Room> =\n | IRoomCacheSortByKeys\n | (ExtractRoomCacheMetadata<RoomType> extends object\n ? keyof ExtractRoomCacheMetadata<RoomType> & string\n : never);\n\nexport interface RegisteredHandlerEvents<RoomType extends Room = any> {\n create: [room: RoomType];\n lock: [room: RoomType];\n unlock: [room: RoomType];\n join: [room: RoomType, client: Client];\n leave: [room: RoomType, client: Client, willDispose: boolean];\n dispose: [room: RoomType];\n 'visibility-change': [room: RoomType, isVisible: boolean];\n 'metadata-change': [room: RoomType];\n}\n\nexport class RegisteredHandler<\n RoomType extends Room = any\n> extends EventEmitter<RegisteredHandlerEvents<RoomType>> {\n '~room': RoomType;\n\n public klass: Type<RoomType>;\n public options: any;\n\n public name: string;\n public filterOptions: Array<FilterByKeys<RoomType>> = [];\n public sortOptions?: SortOptions;\n\n public realtimeListingEnabled: boolean = false;\n\n constructor(klass: Type<RoomType>, options?: any) {\n super();\n\n this.klass = klass;\n this.options = options;\n\n if (typeof(klass) !== 'function') {\n logger.debug('You are likely not importing your room class correctly.');\n throw new Error(`class is expected but ${typeof(klass)} was provided.`);\n }\n }\n\n public enableRealtimeListing() {\n this.realtimeListingEnabled = true;\n this.on('create', (room) => updateLobby(room));\n this.on('lock', (room) => updateLobby(room));\n this.on('unlock', (room) => updateLobby(room));\n this.on('join', (room) => updateLobby(room));\n this.on('leave', (room, _, willDispose) => {\n if (!willDispose) {\n updateLobby(room);\n }\n });\n this.on('visibility-change', (room, isVisible) => updateLobby(room, isVisible));\n this.on('metadata-change', (room) => updateLobby(room));\n this.on('dispose', (room) => updateLobby(room, true));\n return this;\n }\n\n /**\n * Define which fields should be used for filtering rooms.\n * Supports both onCreate options and metadata fields using dot notation.\n *\n * @example\n * // Filter by IRoomCache fields\n * .filterBy(['maxClients'])\n *\n * @example\n * // Filter by metadata fields\n * .filterBy(['difficulty', 'metadata.region'])\n *\n * @example\n * // Mix both\n * .filterBy(['mode', 'difficulty', 'maxClients'])\n */\n public filterBy<T extends FilterByKeys<RoomType>>(\n options: T[]\n ) {\n this.filterOptions = options;\n return this;\n }\n\n /**\n * Define how rooms should be sorted when querying.\n * Supports both room cache fields and metadata fields using dot notation.\n *\n * @example\n * // Sort by number of clients (descending)\n * .sortBy({ clients: -1 })\n *\n * @example\n * // Sort by metadata field\n * .sortBy({ 'metadata.rating': -1 })\n *\n * @example\n * // Multiple sort criteria\n * .sortBy({ 'metadata.skillLevel': 1, clients: -1 })\n */\n public sortBy<T extends SortByKeys<RoomType>>(\n options: { [K in T]: SortOptions[string] }\n ): this {\n this.sortOptions = options as unknown as SortOptions;\n return this;\n }\n\n public getMetadataFromOptions(options: any) {\n const metadata = this.getFilterOptions(options);\n\n if (this.sortOptions) {\n for (const field in this.sortOptions) {\n if (field in options && !(field in metadata)) {\n metadata[field] = options[field];\n }\n }\n }\n\n return Object.keys(metadata).length > 0 ? { metadata } : {};\n }\n\n /**\n * Extract filter options from client options.\n */\n public getFilterOptions(options: any) {\n return this.filterOptions.reduce((prev, curr, i, arr) => {\n const field = String(arr[i]);\n\n // Handle regular (non-metadata) fields\n if (Object.hasOwn(options, field)) {\n if (INVALID_OPTION_KEYS.indexOf(field as any) !== -1) {\n logger.warn(`option \"${field}\" has internal usage and is going to be ignored.`);\n } else {\n prev[field] = options[field];\n }\n }\n\n return prev;\n }, {});\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA6B;AAC7B,oBAAuB;AACvB,kBAAqB;AACrB,mBAA4B;AAMrB,IAAM,sBAA+C;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA+BO,IAAM,oBAAN,cAEG,2BAAgD;AAAA,EAYxD,YAAY,OAAuB,SAAe;AAChD,UAAM;AANR,SAAO,gBAA+C,CAAC;AAGvD,SAAO,yBAAkC;AAKvC,SAAK,QAAQ;AACb,SAAK,UAAU;AAEf,QAAI,OAAO,UAAW,YAAY;AAChC,2BAAO,MAAM,yDAAyD;AACtE,YAAM,IAAI,MAAM,yBAAyB,OAAO,KAAM,gBAAgB;AAAA,IACxE;AAAA,EACF;AAAA,EAEO,wBAAwB;AAC7B,SAAK,yBAAyB;AAC9B,SAAK,GAAG,UAAU,CAAC,aAAS,0BAAY,IAAI,CAAC;AAC7C,SAAK,GAAG,QAAQ,CAAC,aAAS,0BAAY,IAAI,CAAC;AAC3C,SAAK,GAAG,UAAU,CAAC,aAAS,0BAAY,IAAI,CAAC;AAC7C,SAAK,GAAG,QAAQ,CAAC,aAAS,0BAAY,IAAI,CAAC;AAC3C,SAAK,GAAG,SAAS,CAAC,MAAM,GAAG,gBAAgB;AACzC,UAAI,CAAC,aAAa;AAChB,sCAAY,IAAI;AAAA,MAClB;AAAA,IACF,CAAC;AACD,SAAK,GAAG,qBAAqB,CAAC,MAAM,kBAAc,0BAAY,MAAM,SAAS,CAAC;AAC9E,SAAK,GAAG,mBAAmB,CAAC,aAAS,0BAAY,IAAI,CAAC;AACtD,SAAK,GAAG,WAAW,CAAC,aAAS,0BAAY,MAAM,IAAI,CAAC;AACpD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBO,SACL,SACA;AACA,SAAK,gBAAgB;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBO,OACL,SACM;AACN,SAAK,cAAc;AACnB,WAAO;AAAA,EACT;AAAA,EAEO,uBAAuB,SAAc;AAC1C,UAAM,WAAW,KAAK,iBAAiB,OAAO;AAE9C,QAAI,KAAK,aAAa;AACpB,iBAAW,SAAS,KAAK,aAAa;AACpC,YAAI,SAAS,WAAW,EAAE,SAAS,WAAW;AAC5C,mBAAS,KAAK,IAAI,QAAQ,KAAK;AAAA,QACjC;AAAA,MACF;AAAA,IACF;AAEA,WAAO,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA,EAKO,iBAAiB,SAAc;AACpC,WAAO,KAAK,cAAc,OAAO,CAAC,MAAM,MAAM,GAAG,QAAQ;AACvD,YAAM,QAAQ,OAAO,IAAI,CAAC,CAAC;AAG3B,UAAI,OAAO,OAAO,SAAS,KAAK,GAAG;AACjC,YAAI,oBAAoB,QAAQ,KAAY,MAAM,IAAI;AACpD,+BAAO,KAAK,WAAW,KAAK,kDAAkD;AAAA,QAChF,OAAO;AACL,eAAK,KAAK,IAAI,QAAQ,KAAK;AAAA,QAC7B;AAAA,MACF;AAEA,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAAA,EACP;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -98,7 +98,7 @@ var RegisteredHandler = class extends EventEmitter {
|
|
|
98
98
|
getFilterOptions(options) {
|
|
99
99
|
return this.filterOptions.reduce((prev, curr, i, arr) => {
|
|
100
100
|
const field = String(arr[i]);
|
|
101
|
-
if (
|
|
101
|
+
if (Object.hasOwn(options, field)) {
|
|
102
102
|
if (INVALID_OPTION_KEYS.indexOf(field) !== -1) {
|
|
103
103
|
logger.warn(`option "${field}" has internal usage and is going to be ignored.`);
|
|
104
104
|
} else {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/matchmaker/RegisteredHandler.ts"],
|
|
4
|
-
"sourcesContent": ["import { EventEmitter } from 'events';\nimport { logger } from '../Logger.ts';\nimport { Room } from './../Room.ts';\nimport { updateLobby } from './Lobby.ts';\n\nimport type { IRoomCache, SortOptions, IRoomCacheFilterByKeys, IRoomCacheSortByKeys, ExtractRoomCacheMetadata } from './driver.ts';\nimport type { Client } from '../Transport.ts';\nimport type { Type } from \"../utils/Utils.ts\";\n\nexport const INVALID_OPTION_KEYS: Array<keyof IRoomCache> = [\n 'clients',\n 'locked',\n 'private',\n // 'maxClients', - maxClients can be useful as filter options\n 'metadata',\n 'name',\n 'processId',\n 'roomId',\n];\n\n/**\n * Type for filterBy that supports both onCreate options and metadata fields\n */\ntype FilterByKeys<RoomType extends Room> =\n | IRoomCacheFilterByKeys\n | (ExtractRoomCacheMetadata<RoomType> extends object\n ? keyof ExtractRoomCacheMetadata<RoomType> & string\n : never)\n\n/**\n * Type for sortBy that supports room cache fields and metadata fields\n */\ntype SortByKeys<RoomType extends Room> =\n | IRoomCacheSortByKeys\n | (ExtractRoomCacheMetadata<RoomType> extends object\n ? keyof ExtractRoomCacheMetadata<RoomType> & string\n : never);\n\nexport interface RegisteredHandlerEvents<RoomType extends Room = any> {\n create: [room: RoomType];\n lock: [room: RoomType];\n unlock: [room: RoomType];\n join: [room: RoomType, client: Client];\n leave: [room: RoomType, client: Client, willDispose: boolean];\n dispose: [room: RoomType];\n 'visibility-change': [room: RoomType, isVisible: boolean];\n 'metadata-change': [room: RoomType];\n}\n\nexport class RegisteredHandler<\n RoomType extends Room = any\n> extends EventEmitter<RegisteredHandlerEvents<RoomType>> {\n '~room': RoomType;\n\n public klass: Type<RoomType>;\n public options: any;\n\n public name: string;\n public filterOptions: Array<FilterByKeys<RoomType>> = [];\n public sortOptions?: SortOptions;\n\n public realtimeListingEnabled: boolean = false;\n\n constructor(klass: Type<RoomType>, options?: any) {\n super();\n\n this.klass = klass;\n this.options = options;\n\n if (typeof(klass) !== 'function') {\n logger.debug('You are likely not importing your room class correctly.');\n throw new Error(`class is expected but ${typeof(klass)} was provided.`);\n }\n }\n\n public enableRealtimeListing() {\n this.realtimeListingEnabled = true;\n this.on('create', (room) => updateLobby(room));\n this.on('lock', (room) => updateLobby(room));\n this.on('unlock', (room) => updateLobby(room));\n this.on('join', (room) => updateLobby(room));\n this.on('leave', (room, _, willDispose) => {\n if (!willDispose) {\n updateLobby(room);\n }\n });\n this.on('visibility-change', (room, isVisible) => updateLobby(room, isVisible));\n this.on('metadata-change', (room) => updateLobby(room));\n this.on('dispose', (room) => updateLobby(room, true));\n return this;\n }\n\n /**\n * Define which fields should be used for filtering rooms.\n * Supports both onCreate options and metadata fields using dot notation.\n *\n * @example\n * // Filter by IRoomCache fields\n * .filterBy(['maxClients'])\n *\n * @example\n * // Filter by metadata fields\n * .filterBy(['difficulty', 'metadata.region'])\n *\n * @example\n * // Mix both\n * .filterBy(['mode', 'difficulty', 'maxClients'])\n */\n public filterBy<T extends FilterByKeys<RoomType>>(\n options: T[]\n ) {\n this.filterOptions = options;\n return this;\n }\n\n /**\n * Define how rooms should be sorted when querying.\n * Supports both room cache fields and metadata fields using dot notation.\n *\n * @example\n * // Sort by number of clients (descending)\n * .sortBy({ clients: -1 })\n *\n * @example\n * // Sort by metadata field\n * .sortBy({ 'metadata.rating': -1 })\n *\n * @example\n * // Multiple sort criteria\n * .sortBy({ 'metadata.skillLevel': 1, clients: -1 })\n */\n public sortBy<T extends SortByKeys<RoomType>>(\n options: { [K in T]: SortOptions[string] }\n ): this {\n this.sortOptions = options as unknown as SortOptions;\n return this;\n }\n\n public getMetadataFromOptions(options: any) {\n const metadata = this.getFilterOptions(options);\n\n if (this.sortOptions) {\n for (const field in this.sortOptions) {\n if (field in options && !(field in metadata)) {\n metadata[field] = options[field];\n }\n }\n }\n\n return Object.keys(metadata).length > 0 ? { metadata } : {};\n }\n\n /**\n * Extract filter options from client options.\n */\n public getFilterOptions(options: any) {\n return this.filterOptions.reduce((prev, curr, i, arr) => {\n const field = String(arr[i]);\n\n // Handle regular (non-metadata) fields\n if (
|
|
5
|
-
"mappings": ";AAAA,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AACvB,OAAqB;AACrB,SAAS,mBAAmB;AAMrB,IAAM,sBAA+C;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA+BO,IAAM,oBAAN,cAEG,aAAgD;AAAA,EAYxD,YAAY,OAAuB,SAAe;AAChD,UAAM;AANR,SAAO,gBAA+C,CAAC;AAGvD,SAAO,yBAAkC;AAKvC,SAAK,QAAQ;AACb,SAAK,UAAU;AAEf,QAAI,OAAO,UAAW,YAAY;AAChC,aAAO,MAAM,yDAAyD;AACtE,YAAM,IAAI,MAAM,yBAAyB,OAAO,KAAM,gBAAgB;AAAA,IACxE;AAAA,EACF;AAAA,EAEO,wBAAwB;AAC7B,SAAK,yBAAyB;AAC9B,SAAK,GAAG,UAAU,CAAC,SAAS,YAAY,IAAI,CAAC;AAC7C,SAAK,GAAG,QAAQ,CAAC,SAAS,YAAY,IAAI,CAAC;AAC3C,SAAK,GAAG,UAAU,CAAC,SAAS,YAAY,IAAI,CAAC;AAC7C,SAAK,GAAG,QAAQ,CAAC,SAAS,YAAY,IAAI,CAAC;AAC3C,SAAK,GAAG,SAAS,CAAC,MAAM,GAAG,gBAAgB;AACzC,UAAI,CAAC,aAAa;AAChB,oBAAY,IAAI;AAAA,MAClB;AAAA,IACF,CAAC;AACD,SAAK,GAAG,qBAAqB,CAAC,MAAM,cAAc,YAAY,MAAM,SAAS,CAAC;AAC9E,SAAK,GAAG,mBAAmB,CAAC,SAAS,YAAY,IAAI,CAAC;AACtD,SAAK,GAAG,WAAW,CAAC,SAAS,YAAY,MAAM,IAAI,CAAC;AACpD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBO,SACL,SACA;AACA,SAAK,gBAAgB;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBO,OACL,SACM;AACN,SAAK,cAAc;AACnB,WAAO;AAAA,EACT;AAAA,EAEO,uBAAuB,SAAc;AAC1C,UAAM,WAAW,KAAK,iBAAiB,OAAO;AAE9C,QAAI,KAAK,aAAa;AACpB,iBAAW,SAAS,KAAK,aAAa;AACpC,YAAI,SAAS,WAAW,EAAE,SAAS,WAAW;AAC5C,mBAAS,KAAK,IAAI,QAAQ,KAAK;AAAA,QACjC;AAAA,MACF;AAAA,IACF;AAEA,WAAO,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA,EAKO,iBAAiB,SAAc;AACpC,WAAO,KAAK,cAAc,OAAO,CAAC,MAAM,MAAM,GAAG,QAAQ;AACvD,YAAM,QAAQ,OAAO,IAAI,CAAC,CAAC;AAG3B,UAAI,
|
|
4
|
+
"sourcesContent": ["import { EventEmitter } from 'events';\nimport { logger } from '../Logger.ts';\nimport { Room } from './../Room.ts';\nimport { updateLobby } from './Lobby.ts';\n\nimport type { IRoomCache, SortOptions, IRoomCacheFilterByKeys, IRoomCacheSortByKeys, ExtractRoomCacheMetadata } from './driver.ts';\nimport type { Client } from '../Transport.ts';\nimport type { Type } from \"../utils/Utils.ts\";\n\nexport const INVALID_OPTION_KEYS: Array<keyof IRoomCache> = [\n 'clients',\n 'locked',\n 'private',\n // 'maxClients', - maxClients can be useful as filter options\n 'metadata',\n 'name',\n 'processId',\n 'roomId',\n];\n\n/**\n * Type for filterBy that supports both onCreate options and metadata fields\n */\ntype FilterByKeys<RoomType extends Room> =\n | IRoomCacheFilterByKeys\n | (ExtractRoomCacheMetadata<RoomType> extends object\n ? keyof ExtractRoomCacheMetadata<RoomType> & string\n : never)\n\n/**\n * Type for sortBy that supports room cache fields and metadata fields\n */\ntype SortByKeys<RoomType extends Room> =\n | IRoomCacheSortByKeys\n | (ExtractRoomCacheMetadata<RoomType> extends object\n ? keyof ExtractRoomCacheMetadata<RoomType> & string\n : never);\n\nexport interface RegisteredHandlerEvents<RoomType extends Room = any> {\n create: [room: RoomType];\n lock: [room: RoomType];\n unlock: [room: RoomType];\n join: [room: RoomType, client: Client];\n leave: [room: RoomType, client: Client, willDispose: boolean];\n dispose: [room: RoomType];\n 'visibility-change': [room: RoomType, isVisible: boolean];\n 'metadata-change': [room: RoomType];\n}\n\nexport class RegisteredHandler<\n RoomType extends Room = any\n> extends EventEmitter<RegisteredHandlerEvents<RoomType>> {\n '~room': RoomType;\n\n public klass: Type<RoomType>;\n public options: any;\n\n public name: string;\n public filterOptions: Array<FilterByKeys<RoomType>> = [];\n public sortOptions?: SortOptions;\n\n public realtimeListingEnabled: boolean = false;\n\n constructor(klass: Type<RoomType>, options?: any) {\n super();\n\n this.klass = klass;\n this.options = options;\n\n if (typeof(klass) !== 'function') {\n logger.debug('You are likely not importing your room class correctly.');\n throw new Error(`class is expected but ${typeof(klass)} was provided.`);\n }\n }\n\n public enableRealtimeListing() {\n this.realtimeListingEnabled = true;\n this.on('create', (room) => updateLobby(room));\n this.on('lock', (room) => updateLobby(room));\n this.on('unlock', (room) => updateLobby(room));\n this.on('join', (room) => updateLobby(room));\n this.on('leave', (room, _, willDispose) => {\n if (!willDispose) {\n updateLobby(room);\n }\n });\n this.on('visibility-change', (room, isVisible) => updateLobby(room, isVisible));\n this.on('metadata-change', (room) => updateLobby(room));\n this.on('dispose', (room) => updateLobby(room, true));\n return this;\n }\n\n /**\n * Define which fields should be used for filtering rooms.\n * Supports both onCreate options and metadata fields using dot notation.\n *\n * @example\n * // Filter by IRoomCache fields\n * .filterBy(['maxClients'])\n *\n * @example\n * // Filter by metadata fields\n * .filterBy(['difficulty', 'metadata.region'])\n *\n * @example\n * // Mix both\n * .filterBy(['mode', 'difficulty', 'maxClients'])\n */\n public filterBy<T extends FilterByKeys<RoomType>>(\n options: T[]\n ) {\n this.filterOptions = options;\n return this;\n }\n\n /**\n * Define how rooms should be sorted when querying.\n * Supports both room cache fields and metadata fields using dot notation.\n *\n * @example\n * // Sort by number of clients (descending)\n * .sortBy({ clients: -1 })\n *\n * @example\n * // Sort by metadata field\n * .sortBy({ 'metadata.rating': -1 })\n *\n * @example\n * // Multiple sort criteria\n * .sortBy({ 'metadata.skillLevel': 1, clients: -1 })\n */\n public sortBy<T extends SortByKeys<RoomType>>(\n options: { [K in T]: SortOptions[string] }\n ): this {\n this.sortOptions = options as unknown as SortOptions;\n return this;\n }\n\n public getMetadataFromOptions(options: any) {\n const metadata = this.getFilterOptions(options);\n\n if (this.sortOptions) {\n for (const field in this.sortOptions) {\n if (field in options && !(field in metadata)) {\n metadata[field] = options[field];\n }\n }\n }\n\n return Object.keys(metadata).length > 0 ? { metadata } : {};\n }\n\n /**\n * Extract filter options from client options.\n */\n public getFilterOptions(options: any) {\n return this.filterOptions.reduce((prev, curr, i, arr) => {\n const field = String(arr[i]);\n\n // Handle regular (non-metadata) fields\n if (Object.hasOwn(options, field)) {\n if (INVALID_OPTION_KEYS.indexOf(field as any) !== -1) {\n logger.warn(`option \"${field}\" has internal usage and is going to be ignored.`);\n } else {\n prev[field] = options[field];\n }\n }\n\n return prev;\n }, {});\n }\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AACvB,OAAqB;AACrB,SAAS,mBAAmB;AAMrB,IAAM,sBAA+C;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA+BO,IAAM,oBAAN,cAEG,aAAgD;AAAA,EAYxD,YAAY,OAAuB,SAAe;AAChD,UAAM;AANR,SAAO,gBAA+C,CAAC;AAGvD,SAAO,yBAAkC;AAKvC,SAAK,QAAQ;AACb,SAAK,UAAU;AAEf,QAAI,OAAO,UAAW,YAAY;AAChC,aAAO,MAAM,yDAAyD;AACtE,YAAM,IAAI,MAAM,yBAAyB,OAAO,KAAM,gBAAgB;AAAA,IACxE;AAAA,EACF;AAAA,EAEO,wBAAwB;AAC7B,SAAK,yBAAyB;AAC9B,SAAK,GAAG,UAAU,CAAC,SAAS,YAAY,IAAI,CAAC;AAC7C,SAAK,GAAG,QAAQ,CAAC,SAAS,YAAY,IAAI,CAAC;AAC3C,SAAK,GAAG,UAAU,CAAC,SAAS,YAAY,IAAI,CAAC;AAC7C,SAAK,GAAG,QAAQ,CAAC,SAAS,YAAY,IAAI,CAAC;AAC3C,SAAK,GAAG,SAAS,CAAC,MAAM,GAAG,gBAAgB;AACzC,UAAI,CAAC,aAAa;AAChB,oBAAY,IAAI;AAAA,MAClB;AAAA,IACF,CAAC;AACD,SAAK,GAAG,qBAAqB,CAAC,MAAM,cAAc,YAAY,MAAM,SAAS,CAAC;AAC9E,SAAK,GAAG,mBAAmB,CAAC,SAAS,YAAY,IAAI,CAAC;AACtD,SAAK,GAAG,WAAW,CAAC,SAAS,YAAY,MAAM,IAAI,CAAC;AACpD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBO,SACL,SACA;AACA,SAAK,gBAAgB;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBO,OACL,SACM;AACN,SAAK,cAAc;AACnB,WAAO;AAAA,EACT;AAAA,EAEO,uBAAuB,SAAc;AAC1C,UAAM,WAAW,KAAK,iBAAiB,OAAO;AAE9C,QAAI,KAAK,aAAa;AACpB,iBAAW,SAAS,KAAK,aAAa;AACpC,YAAI,SAAS,WAAW,EAAE,SAAS,WAAW;AAC5C,mBAAS,KAAK,IAAI,QAAQ,KAAK;AAAA,QACjC;AAAA,MACF;AAAA,IACF;AAEA,WAAO,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA,EAKO,iBAAiB,SAAc;AACpC,WAAO,KAAK,cAAc,OAAO,CAAC,MAAM,MAAM,GAAG,QAAQ;AACvD,YAAM,QAAQ,OAAO,IAAI,CAAC,CAAC;AAG3B,UAAI,OAAO,OAAO,SAAS,KAAK,GAAG;AACjC,YAAI,oBAAoB,QAAQ,KAAY,MAAM,IAAI;AACpD,iBAAO,KAAK,WAAW,KAAK,kDAAkD;AAAA,QAChF,OAAO;AACL,eAAK,KAAK,IAAI,QAAQ,KAAK;AAAA,QAC7B;AAAA,MACF;AAEA,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAAA,EACP;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -49,20 +49,35 @@ var SchemaSerializer = class {
|
|
|
49
49
|
getFullState(client) {
|
|
50
50
|
if (this.needFullEncode || this.encoder.root.changes.next !== void 0) {
|
|
51
51
|
this.sharedOffsetCache = { offset: 1 };
|
|
52
|
-
this.fullEncodeCache = this.encoder.encodeAll(this.sharedOffsetCache, this.fullEncodeBuffer);
|
|
52
|
+
this.fullEncodeCache = this.encoder.encodeAll(this.sharedOffsetCache, this.fullEncodeBuffer).slice();
|
|
53
|
+
this.fitFullEncodeBuffer(this.sharedOffsetCache.offset);
|
|
53
54
|
this.needFullEncode = false;
|
|
54
55
|
}
|
|
55
56
|
if (this.hasFilters && client?.view) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
this.fullEncodeBuffer
|
|
61
|
-
);
|
|
57
|
+
const it = { ...this.sharedOffsetCache };
|
|
58
|
+
const encoded = this.encoder.encodeAllView(client.view, this.sharedOffsetCache.offset, it, this.fullEncodeBuffer);
|
|
59
|
+
this.fitFullEncodeBuffer(it.offset);
|
|
60
|
+
return encoded;
|
|
62
61
|
} else {
|
|
63
62
|
return this.fullEncodeCache;
|
|
64
63
|
}
|
|
65
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Grow {@link fullEncodeBuffer} once an encode outgrew it. The encoder grows
|
|
67
|
+
* a buffer it is handed for that one encode only, so otherwise every full
|
|
68
|
+
* encode overflows again and per-view encodes read a truncated shared part.
|
|
69
|
+
*
|
|
70
|
+
* `byteLength` is the iterator's final offset, not the returned length:
|
|
71
|
+
* before @colyseus/schema 4.0.27, a per-view encode that overflows returns
|
|
72
|
+
* its bytes cut short at the old buffer size.
|
|
73
|
+
*/
|
|
74
|
+
fitFullEncodeBuffer(byteLength) {
|
|
75
|
+
if (byteLength <= this.fullEncodeBuffer.byteLength) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this.fullEncodeBuffer = new Uint8Array(byteLength + import_schema.Encoder.BUFFER_SIZE);
|
|
79
|
+
this.fullEncodeBuffer.set(this.fullEncodeCache);
|
|
80
|
+
}
|
|
66
81
|
applyPatches(clients) {
|
|
67
82
|
let numClients = clients.length;
|
|
68
83
|
if (numClients === 0) {
|
|
@@ -96,12 +111,13 @@ var SchemaSerializer = class {
|
|
|
96
111
|
this.encoder.sharedBuffer[0] = import_shared_types.Protocol.ROOM_STATE_PATCH;
|
|
97
112
|
const encodedChanges = this.encoder.encode(it);
|
|
98
113
|
if (!this.hasFilters) {
|
|
114
|
+
const frame = Buffer.from(encodedChanges);
|
|
99
115
|
while (numClients--) {
|
|
100
116
|
const client = clients[numClients];
|
|
101
117
|
if (client.state !== import_Transport.ClientState.JOINED) {
|
|
102
118
|
continue;
|
|
103
119
|
}
|
|
104
|
-
client.raw(
|
|
120
|
+
client.raw(frame);
|
|
105
121
|
}
|
|
106
122
|
} else {
|
|
107
123
|
const sharedOffset = it.offset;
|
|
@@ -113,7 +129,7 @@ var SchemaSerializer = class {
|
|
|
113
129
|
const view = client.view || SHARED_VIEW;
|
|
114
130
|
let encodedView = this.encodedViews.get(view);
|
|
115
131
|
if (encodedView === void 0) {
|
|
116
|
-
encodedView = view === SHARED_VIEW ? encodedChanges : this.encoder.encodeView(client.view, sharedOffset, it);
|
|
132
|
+
encodedView = view === SHARED_VIEW ? Buffer.from(encodedChanges) : this.encoder.encodeView(client.view, sharedOffset, it);
|
|
117
133
|
this.encodedViews.set(view, encodedView);
|
|
118
134
|
}
|
|
119
135
|
client.raw(encodedView);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/serializer/SchemaSerializer.ts"],
|
|
4
|
-
"sourcesContent": ["import { Protocol } from '@colyseus/shared-types';\n\nimport type { Serializer } from './Serializer.ts';\nimport { type Client, ClientState } from '../Transport.ts';\n\nimport { type Iterator, Encoder, dumpChanges, Reflection, Schema, StateView } from '@colyseus/schema';\nimport { debugPatch } from '../Debug.ts';\n\nconst SHARED_VIEW = {};\n\nexport class SchemaSerializer<T extends Schema> implements Serializer<T> {\n public id = 'schema';\n\n protected encoder: Encoder<T>;\n protected hasFilters: boolean = false;\n\n protected handshakeCache: Uint8Array;\n\n // flag to avoid re-encoding full state if no changes were made\n protected needFullEncode: boolean = true;\n\n // TODO: make this optional. allocating a new buffer for each room may not be always necessary.\n protected fullEncodeBuffer: Uint8Array = new Uint8Array(Encoder.BUFFER_SIZE);\n protected fullEncodeCache: Uint8Array;\n protected sharedOffsetCache: Iterator = { offset: 0 };\n\n protected encodedViews: Map<StateView | typeof SHARED_VIEW, Uint8Array>;\n\n public reset(newState: T & Schema) {\n this.encoder = new Encoder(newState);\n this.hasFilters = this.encoder.context.hasFilters;\n\n // cache ROOM_STATE byte as part of the encoded buffer\n this.fullEncodeBuffer[0] = Protocol.ROOM_STATE;\n\n if (this.hasFilters) {\n this.encodedViews = new Map();\n }\n }\n\n public getFullState(client?: Client) {\n if (this.needFullEncode || this.encoder.root.changes.next !== undefined) {\n this.sharedOffsetCache = { offset: 1 };\n this.fullEncodeCache = this.encoder.encodeAll(this.sharedOffsetCache, this.fullEncodeBuffer);\n this.needFullEncode = false;\n }\n\n if (this.hasFilters && client?.view) {\n
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAAyB;AAGzB,uBAAyC;AAEzC,oBAAmF;AACnF,mBAA2B;AAE3B,IAAM,cAAc,CAAC;AAEd,IAAM,mBAAN,MAAkE;AAAA,EAAlE;AACL,SAAO,KAAK;AAGZ,SAAU,aAAsB;AAKhC;AAAA,SAAU,iBAA0B;AAGpC;AAAA,SAAU,mBAA+B,IAAI,WAAW,sBAAQ,WAAW;AAE3E,SAAU,oBAA8B,EAAE,QAAQ,EAAE;AAAA;AAAA,EAI7C,MAAM,UAAsB;AACjC,SAAK,UAAU,IAAI,sBAAQ,QAAQ;AACnC,SAAK,aAAa,KAAK,QAAQ,QAAQ;AAGvC,SAAK,iBAAiB,CAAC,IAAI,6BAAS;AAEpC,QAAI,KAAK,YAAY;AACnB,WAAK,eAAe,oBAAI,IAAI;AAAA,IAC9B;AAAA,EACF;AAAA,EAEO,aAAa,QAAiB;AACnC,QAAI,KAAK,kBAAkB,KAAK,QAAQ,KAAK,QAAQ,SAAS,QAAW;AACvE,WAAK,oBAAoB,EAAE,QAAQ,EAAE;
|
|
4
|
+
"sourcesContent": ["import { Protocol } from '@colyseus/shared-types';\n\nimport type { Serializer } from './Serializer.ts';\nimport { type Client, ClientState } from '../Transport.ts';\n\nimport { type Iterator, Encoder, dumpChanges, Reflection, Schema, StateView } from '@colyseus/schema';\nimport { debugPatch } from '../Debug.ts';\n\nconst SHARED_VIEW = {};\n\nexport class SchemaSerializer<T extends Schema> implements Serializer<T> {\n public id = 'schema';\n\n protected encoder: Encoder<T>;\n protected hasFilters: boolean = false;\n\n protected handshakeCache: Uint8Array;\n\n // flag to avoid re-encoding full state if no changes were made\n protected needFullEncode: boolean = true;\n\n // TODO: make this optional. allocating a new buffer for each room may not be always necessary.\n protected fullEncodeBuffer: Uint8Array = new Uint8Array(Encoder.BUFFER_SIZE);\n protected fullEncodeCache: Uint8Array;\n protected sharedOffsetCache: Iterator = { offset: 0 };\n\n protected encodedViews: Map<StateView | typeof SHARED_VIEW, Uint8Array>;\n\n public reset(newState: T & Schema) {\n this.encoder = new Encoder(newState);\n this.hasFilters = this.encoder.context.hasFilters;\n\n // cache ROOM_STATE byte as part of the encoded buffer\n this.fullEncodeBuffer[0] = Protocol.ROOM_STATE;\n\n if (this.hasFilters) {\n this.encodedViews = new Map();\n }\n }\n\n public getFullState(client?: Client) {\n if (this.needFullEncode || this.encoder.root.changes.next !== undefined) {\n this.sharedOffsetCache = { offset: 1 };\n // copy: the next full encode reuses the buffer (slice(): a pooled Buffer would pin 8KB)\n this.fullEncodeCache = this.encoder.encodeAll(this.sharedOffsetCache, this.fullEncodeBuffer).slice();\n this.fitFullEncodeBuffer(this.sharedOffsetCache.offset);\n this.needFullEncode = false;\n }\n\n if (this.hasFilters && client?.view) {\n const it = { ...this.sharedOffsetCache };\n const encoded = this.encoder.encodeAllView(client.view, this.sharedOffsetCache.offset, it, this.fullEncodeBuffer);\n this.fitFullEncodeBuffer(it.offset);\n return encoded;\n\n } else {\n return this.fullEncodeCache;\n }\n }\n\n /**\n * Grow {@link fullEncodeBuffer} once an encode outgrew it. The encoder grows\n * a buffer it is handed for that one encode only, so otherwise every full\n * encode overflows again and per-view encodes read a truncated shared part.\n *\n * `byteLength` is the iterator's final offset, not the returned length:\n * before @colyseus/schema 4.0.27, a per-view encode that overflows returns\n * its bytes cut short at the old buffer size.\n */\n protected fitFullEncodeBuffer(byteLength: number) {\n if (byteLength <= this.fullEncodeBuffer.byteLength) { return; }\n this.fullEncodeBuffer = new Uint8Array(byteLength + Encoder.BUFFER_SIZE);\n this.fullEncodeBuffer.set(this.fullEncodeCache);\n }\n\n public applyPatches(clients: Client[]) {\n let numClients = clients.length;\n\n if (numClients === 0) {\n if (this.encoder.hasChanges) {\n // if there are changes but no clients, we need to encode full state on next patch\n this.needFullEncode = true;\n }\n // skip patching and clear changes\n this.encoder.discardChanges();\n return false;\n }\n\n if (!this.encoder.hasChanges) {\n\n // check if views have changes (manual add() or remove() items)\n if (this.hasFilters) {\n //\n // FIXME: refactor this to avoid duplicating code.\n //\n // it's probably better to have 2 different 'applyPatches' methods.\n // (one for handling state with filters, and another for handling state without filters)\n //\n const clientsWithViewChange = clients.filter((client) => {\n return client.state === ClientState.JOINED && client.view?.changes.size > 0\n });\n\n if (clientsWithViewChange.length > 0) {\n const it: Iterator = { offset: 1 };\n\n const sharedOffset = it.offset;\n this.encoder.sharedBuffer[0] = Protocol.ROOM_STATE_PATCH;\n\n clientsWithViewChange.forEach((client) => {\n client.raw(this.encoder.encodeView(client.view, sharedOffset, it));\n });\n }\n }\n\n // skip patching state if:\n // - no clients are connected\n // - no changes were made\n // - no \"filtered changes\" were made when using filters\n return false;\n }\n\n this.needFullEncode = true;\n\n // dump changes for patch debugging\n if (debugPatch.enabled) {\n (debugPatch as any).dumpChanges = dumpChanges(this.encoder.state);\n }\n\n // get patch bytes\n const it: Iterator = { offset: 1 };\n this.encoder.sharedBuffer[0] = Protocol.ROOM_STATE_PATCH;\n\n // encode changes once, for all clients\n const encodedChanges = this.encoder.encode(it);\n\n if (!this.hasFilters) {\n // copy once for all clients: a transport may still hold it after the next encode (see Client.raw)\n const frame = Buffer.from(encodedChanges);\n\n while (numClients--) {\n const client = clients[numClients];\n\n //\n // FIXME: avoid this check for each client\n //\n if (client.state !== ClientState.JOINED) {\n continue;\n }\n\n client.raw(frame);\n }\n\n } else {\n // cache shared offset\n const sharedOffset = it.offset;\n\n // encode state multiple times, for each client\n while (numClients--) {\n const client = clients[numClients];\n\n //\n // FIXME: avoid this check for each client\n //\n if (client.state !== ClientState.JOINED) {\n continue;\n }\n\n const view = client.view || SHARED_VIEW;\n\n let encodedView = this.encodedViews.get(view);\n\n // allow to pass the same encoded view for multiple clients\n if (encodedView === undefined) {\n encodedView = (view === SHARED_VIEW)\n ? Buffer.from(encodedChanges) // encodeView already returns a copy\n : this.encoder.encodeView(client.view, sharedOffset, it);\n this.encodedViews.set(view, encodedView);\n }\n\n client.raw(encodedView);\n }\n\n // clear views\n this.encodedViews.clear();\n }\n\n // discard changes after sending\n this.encoder.discardChanges();\n\n // debug patches\n if (debugPatch.enabled) {\n debugPatch(\n '%d bytes sent to %d clients, %j',\n encodedChanges.length,\n clients.length,\n (debugPatch as any).dumpChanges,\n );\n }\n\n return true;\n }\n\n public handshake() {\n /**\n * Cache handshake to avoid encoding it for each client joining\n */\n if (!this.handshakeCache) {\n //\n // TODO: re-use handshake buffer for all rooms of same type (?)\n //\n this.handshakeCache = (this.encoder.state && Reflection.encode(this.encoder));\n }\n\n return this.handshakeCache;\n }\n\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["it"]
|
|
7
7
|
}
|
|
@@ -14,6 +14,16 @@ export declare class SchemaSerializer<T extends Schema> implements Serializer<T>
|
|
|
14
14
|
protected encodedViews: Map<StateView | typeof SHARED_VIEW, Uint8Array>;
|
|
15
15
|
reset(newState: T & Schema): void;
|
|
16
16
|
getFullState(client?: Client): Uint8Array<ArrayBufferLike>;
|
|
17
|
+
/**
|
|
18
|
+
* Grow {@link fullEncodeBuffer} once an encode outgrew it. The encoder grows
|
|
19
|
+
* a buffer it is handed for that one encode only, so otherwise every full
|
|
20
|
+
* encode overflows again and per-view encodes read a truncated shared part.
|
|
21
|
+
*
|
|
22
|
+
* `byteLength` is the iterator's final offset, not the returned length:
|
|
23
|
+
* before @colyseus/schema 4.0.27, a per-view encode that overflows returns
|
|
24
|
+
* its bytes cut short at the old buffer size.
|
|
25
|
+
*/
|
|
26
|
+
protected fitFullEncodeBuffer(byteLength: number): void;
|
|
17
27
|
applyPatches(clients: Client[]): boolean;
|
|
18
28
|
handshake(): Uint8Array<ArrayBufferLike>;
|
|
19
29
|
}
|
|
@@ -25,20 +25,35 @@ var SchemaSerializer = class {
|
|
|
25
25
|
getFullState(client) {
|
|
26
26
|
if (this.needFullEncode || this.encoder.root.changes.next !== void 0) {
|
|
27
27
|
this.sharedOffsetCache = { offset: 1 };
|
|
28
|
-
this.fullEncodeCache = this.encoder.encodeAll(this.sharedOffsetCache, this.fullEncodeBuffer);
|
|
28
|
+
this.fullEncodeCache = this.encoder.encodeAll(this.sharedOffsetCache, this.fullEncodeBuffer).slice();
|
|
29
|
+
this.fitFullEncodeBuffer(this.sharedOffsetCache.offset);
|
|
29
30
|
this.needFullEncode = false;
|
|
30
31
|
}
|
|
31
32
|
if (this.hasFilters && client?.view) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.fullEncodeBuffer
|
|
37
|
-
);
|
|
33
|
+
const it = { ...this.sharedOffsetCache };
|
|
34
|
+
const encoded = this.encoder.encodeAllView(client.view, this.sharedOffsetCache.offset, it, this.fullEncodeBuffer);
|
|
35
|
+
this.fitFullEncodeBuffer(it.offset);
|
|
36
|
+
return encoded;
|
|
38
37
|
} else {
|
|
39
38
|
return this.fullEncodeCache;
|
|
40
39
|
}
|
|
41
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Grow {@link fullEncodeBuffer} once an encode outgrew it. The encoder grows
|
|
43
|
+
* a buffer it is handed for that one encode only, so otherwise every full
|
|
44
|
+
* encode overflows again and per-view encodes read a truncated shared part.
|
|
45
|
+
*
|
|
46
|
+
* `byteLength` is the iterator's final offset, not the returned length:
|
|
47
|
+
* before @colyseus/schema 4.0.27, a per-view encode that overflows returns
|
|
48
|
+
* its bytes cut short at the old buffer size.
|
|
49
|
+
*/
|
|
50
|
+
fitFullEncodeBuffer(byteLength) {
|
|
51
|
+
if (byteLength <= this.fullEncodeBuffer.byteLength) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
this.fullEncodeBuffer = new Uint8Array(byteLength + Encoder.BUFFER_SIZE);
|
|
55
|
+
this.fullEncodeBuffer.set(this.fullEncodeCache);
|
|
56
|
+
}
|
|
42
57
|
applyPatches(clients) {
|
|
43
58
|
let numClients = clients.length;
|
|
44
59
|
if (numClients === 0) {
|
|
@@ -72,12 +87,13 @@ var SchemaSerializer = class {
|
|
|
72
87
|
this.encoder.sharedBuffer[0] = Protocol.ROOM_STATE_PATCH;
|
|
73
88
|
const encodedChanges = this.encoder.encode(it);
|
|
74
89
|
if (!this.hasFilters) {
|
|
90
|
+
const frame = Buffer.from(encodedChanges);
|
|
75
91
|
while (numClients--) {
|
|
76
92
|
const client = clients[numClients];
|
|
77
93
|
if (client.state !== ClientState.JOINED) {
|
|
78
94
|
continue;
|
|
79
95
|
}
|
|
80
|
-
client.raw(
|
|
96
|
+
client.raw(frame);
|
|
81
97
|
}
|
|
82
98
|
} else {
|
|
83
99
|
const sharedOffset = it.offset;
|
|
@@ -89,7 +105,7 @@ var SchemaSerializer = class {
|
|
|
89
105
|
const view = client.view || SHARED_VIEW;
|
|
90
106
|
let encodedView = this.encodedViews.get(view);
|
|
91
107
|
if (encodedView === void 0) {
|
|
92
|
-
encodedView = view === SHARED_VIEW ? encodedChanges : this.encoder.encodeView(client.view, sharedOffset, it);
|
|
108
|
+
encodedView = view === SHARED_VIEW ? Buffer.from(encodedChanges) : this.encoder.encodeView(client.view, sharedOffset, it);
|
|
93
109
|
this.encodedViews.set(view, encodedView);
|
|
94
110
|
}
|
|
95
111
|
client.raw(encodedView);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/serializer/SchemaSerializer.ts"],
|
|
4
|
-
"sourcesContent": ["import { Protocol } from '@colyseus/shared-types';\n\nimport type { Serializer } from './Serializer.ts';\nimport { type Client, ClientState } from '../Transport.ts';\n\nimport { type Iterator, Encoder, dumpChanges, Reflection, Schema, StateView } from '@colyseus/schema';\nimport { debugPatch } from '../Debug.ts';\n\nconst SHARED_VIEW = {};\n\nexport class SchemaSerializer<T extends Schema> implements Serializer<T> {\n public id = 'schema';\n\n protected encoder: Encoder<T>;\n protected hasFilters: boolean = false;\n\n protected handshakeCache: Uint8Array;\n\n // flag to avoid re-encoding full state if no changes were made\n protected needFullEncode: boolean = true;\n\n // TODO: make this optional. allocating a new buffer for each room may not be always necessary.\n protected fullEncodeBuffer: Uint8Array = new Uint8Array(Encoder.BUFFER_SIZE);\n protected fullEncodeCache: Uint8Array;\n protected sharedOffsetCache: Iterator = { offset: 0 };\n\n protected encodedViews: Map<StateView | typeof SHARED_VIEW, Uint8Array>;\n\n public reset(newState: T & Schema) {\n this.encoder = new Encoder(newState);\n this.hasFilters = this.encoder.context.hasFilters;\n\n // cache ROOM_STATE byte as part of the encoded buffer\n this.fullEncodeBuffer[0] = Protocol.ROOM_STATE;\n\n if (this.hasFilters) {\n this.encodedViews = new Map();\n }\n }\n\n public getFullState(client?: Client) {\n if (this.needFullEncode || this.encoder.root.changes.next !== undefined) {\n this.sharedOffsetCache = { offset: 1 };\n this.fullEncodeCache = this.encoder.encodeAll(this.sharedOffsetCache, this.fullEncodeBuffer);\n this.needFullEncode = false;\n }\n\n if (this.hasFilters && client?.view) {\n
|
|
5
|
-
"mappings": ";AAAA,SAAS,gBAAgB;AAGzB,SAAsB,mBAAmB;AAEzC,SAAwB,SAAS,aAAa,kBAAqC;AACnF,SAAS,kBAAkB;AAE3B,IAAM,cAAc,CAAC;AAEd,IAAM,mBAAN,MAAkE;AAAA,EAAlE;AACL,SAAO,KAAK;AAGZ,SAAU,aAAsB;AAKhC;AAAA,SAAU,iBAA0B;AAGpC;AAAA,SAAU,mBAA+B,IAAI,WAAW,QAAQ,WAAW;AAE3E,SAAU,oBAA8B,EAAE,QAAQ,EAAE;AAAA;AAAA,EAI7C,MAAM,UAAsB;AACjC,SAAK,UAAU,IAAI,QAAQ,QAAQ;AACnC,SAAK,aAAa,KAAK,QAAQ,QAAQ;AAGvC,SAAK,iBAAiB,CAAC,IAAI,SAAS;AAEpC,QAAI,KAAK,YAAY;AACnB,WAAK,eAAe,oBAAI,IAAI;AAAA,IAC9B;AAAA,EACF;AAAA,EAEO,aAAa,QAAiB;AACnC,QAAI,KAAK,kBAAkB,KAAK,QAAQ,KAAK,QAAQ,SAAS,QAAW;AACvE,WAAK,oBAAoB,EAAE,QAAQ,EAAE;
|
|
4
|
+
"sourcesContent": ["import { Protocol } from '@colyseus/shared-types';\n\nimport type { Serializer } from './Serializer.ts';\nimport { type Client, ClientState } from '../Transport.ts';\n\nimport { type Iterator, Encoder, dumpChanges, Reflection, Schema, StateView } from '@colyseus/schema';\nimport { debugPatch } from '../Debug.ts';\n\nconst SHARED_VIEW = {};\n\nexport class SchemaSerializer<T extends Schema> implements Serializer<T> {\n public id = 'schema';\n\n protected encoder: Encoder<T>;\n protected hasFilters: boolean = false;\n\n protected handshakeCache: Uint8Array;\n\n // flag to avoid re-encoding full state if no changes were made\n protected needFullEncode: boolean = true;\n\n // TODO: make this optional. allocating a new buffer for each room may not be always necessary.\n protected fullEncodeBuffer: Uint8Array = new Uint8Array(Encoder.BUFFER_SIZE);\n protected fullEncodeCache: Uint8Array;\n protected sharedOffsetCache: Iterator = { offset: 0 };\n\n protected encodedViews: Map<StateView | typeof SHARED_VIEW, Uint8Array>;\n\n public reset(newState: T & Schema) {\n this.encoder = new Encoder(newState);\n this.hasFilters = this.encoder.context.hasFilters;\n\n // cache ROOM_STATE byte as part of the encoded buffer\n this.fullEncodeBuffer[0] = Protocol.ROOM_STATE;\n\n if (this.hasFilters) {\n this.encodedViews = new Map();\n }\n }\n\n public getFullState(client?: Client) {\n if (this.needFullEncode || this.encoder.root.changes.next !== undefined) {\n this.sharedOffsetCache = { offset: 1 };\n // copy: the next full encode reuses the buffer (slice(): a pooled Buffer would pin 8KB)\n this.fullEncodeCache = this.encoder.encodeAll(this.sharedOffsetCache, this.fullEncodeBuffer).slice();\n this.fitFullEncodeBuffer(this.sharedOffsetCache.offset);\n this.needFullEncode = false;\n }\n\n if (this.hasFilters && client?.view) {\n const it = { ...this.sharedOffsetCache };\n const encoded = this.encoder.encodeAllView(client.view, this.sharedOffsetCache.offset, it, this.fullEncodeBuffer);\n this.fitFullEncodeBuffer(it.offset);\n return encoded;\n\n } else {\n return this.fullEncodeCache;\n }\n }\n\n /**\n * Grow {@link fullEncodeBuffer} once an encode outgrew it. The encoder grows\n * a buffer it is handed for that one encode only, so otherwise every full\n * encode overflows again and per-view encodes read a truncated shared part.\n *\n * `byteLength` is the iterator's final offset, not the returned length:\n * before @colyseus/schema 4.0.27, a per-view encode that overflows returns\n * its bytes cut short at the old buffer size.\n */\n protected fitFullEncodeBuffer(byteLength: number) {\n if (byteLength <= this.fullEncodeBuffer.byteLength) { return; }\n this.fullEncodeBuffer = new Uint8Array(byteLength + Encoder.BUFFER_SIZE);\n this.fullEncodeBuffer.set(this.fullEncodeCache);\n }\n\n public applyPatches(clients: Client[]) {\n let numClients = clients.length;\n\n if (numClients === 0) {\n if (this.encoder.hasChanges) {\n // if there are changes but no clients, we need to encode full state on next patch\n this.needFullEncode = true;\n }\n // skip patching and clear changes\n this.encoder.discardChanges();\n return false;\n }\n\n if (!this.encoder.hasChanges) {\n\n // check if views have changes (manual add() or remove() items)\n if (this.hasFilters) {\n //\n // FIXME: refactor this to avoid duplicating code.\n //\n // it's probably better to have 2 different 'applyPatches' methods.\n // (one for handling state with filters, and another for handling state without filters)\n //\n const clientsWithViewChange = clients.filter((client) => {\n return client.state === ClientState.JOINED && client.view?.changes.size > 0\n });\n\n if (clientsWithViewChange.length > 0) {\n const it: Iterator = { offset: 1 };\n\n const sharedOffset = it.offset;\n this.encoder.sharedBuffer[0] = Protocol.ROOM_STATE_PATCH;\n\n clientsWithViewChange.forEach((client) => {\n client.raw(this.encoder.encodeView(client.view, sharedOffset, it));\n });\n }\n }\n\n // skip patching state if:\n // - no clients are connected\n // - no changes were made\n // - no \"filtered changes\" were made when using filters\n return false;\n }\n\n this.needFullEncode = true;\n\n // dump changes for patch debugging\n if (debugPatch.enabled) {\n (debugPatch as any).dumpChanges = dumpChanges(this.encoder.state);\n }\n\n // get patch bytes\n const it: Iterator = { offset: 1 };\n this.encoder.sharedBuffer[0] = Protocol.ROOM_STATE_PATCH;\n\n // encode changes once, for all clients\n const encodedChanges = this.encoder.encode(it);\n\n if (!this.hasFilters) {\n // copy once for all clients: a transport may still hold it after the next encode (see Client.raw)\n const frame = Buffer.from(encodedChanges);\n\n while (numClients--) {\n const client = clients[numClients];\n\n //\n // FIXME: avoid this check for each client\n //\n if (client.state !== ClientState.JOINED) {\n continue;\n }\n\n client.raw(frame);\n }\n\n } else {\n // cache shared offset\n const sharedOffset = it.offset;\n\n // encode state multiple times, for each client\n while (numClients--) {\n const client = clients[numClients];\n\n //\n // FIXME: avoid this check for each client\n //\n if (client.state !== ClientState.JOINED) {\n continue;\n }\n\n const view = client.view || SHARED_VIEW;\n\n let encodedView = this.encodedViews.get(view);\n\n // allow to pass the same encoded view for multiple clients\n if (encodedView === undefined) {\n encodedView = (view === SHARED_VIEW)\n ? Buffer.from(encodedChanges) // encodeView already returns a copy\n : this.encoder.encodeView(client.view, sharedOffset, it);\n this.encodedViews.set(view, encodedView);\n }\n\n client.raw(encodedView);\n }\n\n // clear views\n this.encodedViews.clear();\n }\n\n // discard changes after sending\n this.encoder.discardChanges();\n\n // debug patches\n if (debugPatch.enabled) {\n debugPatch(\n '%d bytes sent to %d clients, %j',\n encodedChanges.length,\n clients.length,\n (debugPatch as any).dumpChanges,\n );\n }\n\n return true;\n }\n\n public handshake() {\n /**\n * Cache handshake to avoid encoding it for each client joining\n */\n if (!this.handshakeCache) {\n //\n // TODO: re-use handshake buffer for all rooms of same type (?)\n //\n this.handshakeCache = (this.encoder.state && Reflection.encode(this.encoder));\n }\n\n return this.handshakeCache;\n }\n\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["it"]
|
|
7
7
|
}
|
package/build/utils/Utils.cjs
CHANGED
|
@@ -105,7 +105,7 @@ function merge(a, ...objs) {
|
|
|
105
105
|
for (let i = 0, len = objs.length; i < len; i++) {
|
|
106
106
|
const b = objs[i];
|
|
107
107
|
for (const key in b) {
|
|
108
|
-
if (
|
|
108
|
+
if (key !== "__proto__" && Object.hasOwn(b, key)) {
|
|
109
109
|
a[key] = b[key];
|
|
110
110
|
}
|
|
111
111
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/Utils.ts"],
|
|
4
|
-
"sourcesContent": ["import { nanoid } from 'nanoid';\nimport { type RoomException, type RoomMethodName } from '../errors/RoomExceptions.ts';\n\nimport { debugAndPrintError, debugMatchMaking } from '../Debug.ts';\n\nexport type Type<T> = new (...args: any[]) => T;\nexport type MethodName<T> = string & {\n [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never\n}[keyof T];\n\n/**\n * Utility type that extracts the return type of a method or the type of a property\n * from a given class/object type.\n *\n * - If the key is a method, returns the awaited return type of that method\n * - If the key is a property, returns the type of that property\n */\nexport type ExtractMethodOrPropertyType<\n TClass,\n TKey extends keyof TClass\n> = TClass[TKey] extends (...args: any[]) => infer R\n ? Awaited<R>\n : TClass[TKey];\n\n/**\n * Return type of `remoteRoomCall()`.\n *\n * Resolves to the method's awaited return type (or the property's type) when\n * the method name was captured as a literal type. Falls back to `any` when it\n * wasn't \u2014 e.g. `remoteRoomCall<MyRoom>(...)` with only the room type given:\n * TypeScript applies the `TMethod` default instead of inferring the literal\n * once an explicit type argument list is present (microsoft/TypeScript#26242).\n * Pass both type arguments (`remoteRoomCall<MyRoom, 'myMethod'>`) for a\n * precise return type.\n */\nexport type RemoteRoomCallReturn<\n TRoom,\n TMethod extends keyof TRoom\n> = keyof TRoom extends TMethod\n ? any\n : ExtractMethodOrPropertyType<TRoom, TMethod>;\n\n// remote room call timeouts\nexport const REMOTE_ROOM_SHORT_TIMEOUT = Number(process.env.COLYSEUS_PRESENCE_SHORT_TIMEOUT || 2000);\nexport const MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME = Number(process.env.COLYSEUS_MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME || 0.5);\n\n// a peer that removes this process's room listings leaves a receipt. this\n// process looks for it every INTERVAL (ms) and restores them. the TTL (seconds)\n// only bounds what dead processes leave behind.\nexport const SWEPT_CHECK_INTERVAL = Number(process.env.COLYSEUS_SWEPT_CHECK_INTERVAL || 5000);\nexport const SWEPT_RECEIPT_TTL = Number(process.env.COLYSEUS_SWEPT_RECEIPT_TTL || 600);\n\nexport function generateId(length: number = 9) {\n return nanoid(length);\n}\n\nexport function getBearerToken(authHeader: string) {\n return (authHeader && authHeader.startsWith(\"Bearer \") && authHeader.substring(7, authHeader.length)) || undefined;\n}\n\n// nodemon sends SIGUSR2 before reloading\n// (https://github.com/remy/nodemon#controlling-shutdown-of-your-script)\n//\nconst signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGUSR2'];\n\nexport function registerGracefulShutdown(callback: (err?: Error) => void) {\n /**\n * Gracefully shutdown on uncaught errors\n */\n process.on('uncaughtException', (err) => {\n debugAndPrintError(err);\n callback(err);\n });\n\n signals.forEach((signal) =>\n process.once(signal, () => callback()));\n}\n\nexport function retry<T = any>(\n cb: Function,\n maxRetries: number = 3,\n errorWhiteList: any[] = [],\n retries: number = 0,\n) {\n return new Promise<T>((resolve, reject) => {\n cb()\n .then(resolve)\n .catch((e: any) => {\n if (\n errorWhiteList.indexOf(e.constructor) !== -1 &&\n retries++ < maxRetries\n ) {\n setTimeout(() => {\n debugMatchMaking(\"retrying due to error (error: %s, retries: %s, maxRetries: %s)\", e.message, retries, maxRetries);\n retry<T>(cb, maxRetries, errorWhiteList, retries).\n then(resolve).\n catch((e2) => reject(e2));\n }, Math.floor(Math.random() * Math.pow(2, retries) * 400));\n\n } else {\n reject(e);\n }\n });\n });\n}\n\nexport function spliceOne(arr: any[], index: number): boolean {\n // manually splice availableRooms array\n // http://jsperf.com/manual-splice\n if (index === -1 || index >= arr.length) {\n return false;\n }\n\n const len = arr.length - 1;\n for (let i = index; i < len; i++) {\n arr[i] = arr[i + 1];\n }\n\n arr.length = len;\n return true;\n}\n\nexport class Deferred<T = any> {\n public promise: Promise<T>;\n\n public resolve: Function;\n public reject: Function;\n\n constructor(promise?: Promise<T>) {\n this.promise = promise ?? new Promise<T>((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n\n public then(onFulfilled?: (value: T) => any, onRejected?: (reason: any) => any) {\n return this.promise.then(onFulfilled, onRejected);\n }\n\n public catch(func: (value: any) => any) {\n return this.promise.catch(func);\n }\n\n static reject (reason?: any) {\n return new Deferred(Promise.reject(reason));\n }\n\n static resolve<T = any>(value?: T) {\n return new Deferred<T>(Promise.resolve(value));\n }\n\n}\n\nexport function merge(a: any, ...objs: any[]): any {\n for (let i = 0, len = objs.length; i < len; i++) {\n const b = objs[i];\n for (const key in b) {\n if (
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAuB;AACvB,4BAAwD;AAExD,mBAAqD;AAwC9C,IAAM,4BAA4B,OAAO,QAAQ,IAAI,mCAAmC,GAAI;AAC5F,IAAM,uCAAuC,OAAO,QAAQ,IAAI,iDAAiD,GAAG;AAKpH,IAAM,uBAAuB,OAAO,QAAQ,IAAI,iCAAiC,GAAI;AACrF,IAAM,oBAAoB,OAAO,QAAQ,IAAI,8BAA8B,GAAG;AAE9E,SAAS,WAAW,SAAiB,GAAG;AAC7C,aAAO,sBAAO,MAAM;AACtB;AAEO,SAAS,eAAe,YAAoB;AACjD,SAAQ,cAAc,WAAW,WAAW,SAAS,KAAK,WAAW,UAAU,GAAG,WAAW,MAAM,KAAM;AAC3G;AAKA,IAAM,UAA4B,CAAC,UAAU,WAAW,SAAS;AAE1D,SAAS,yBAAyB,UAAiC;AAIxE,UAAQ,GAAG,qBAAqB,CAAC,QAAQ;AACvC,yCAAmB,GAAG;AACtB,aAAS,GAAG;AAAA,EACd,CAAC;AAED,UAAQ,QAAQ,CAAC,WACf,QAAQ,KAAK,QAAQ,MAAM,SAAS,CAAC,CAAC;AAC1C;AAEO,SAAS,MACd,IACA,aAAqB,GACrB,iBAAwB,CAAC,GACzB,UAAkB,GAClB;AACA,SAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,OAAG,EACA,KAAK,OAAO,EACZ,MAAM,CAAC,MAAW;AACjB,UACE,eAAe,QAAQ,EAAE,WAAW,MAAM,MAC1C,YAAY,YACZ;AACA,mBAAW,MAAM;AACf,6CAAiB,kEAAkE,EAAE,SAAS,SAAS,UAAU;AACjH,gBAAS,IAAI,YAAY,gBAAgB,OAAO,EAC9C,KAAK,OAAO,EACZ,MAAM,CAAC,OAAO,OAAO,EAAE,CAAC;AAAA,QAC5B,GAAG,KAAK,MAAM,KAAK,OAAO,IAAI,KAAK,IAAI,GAAG,OAAO,IAAI,GAAG,CAAC;AAAA,MAE3D,OAAO;AACL,eAAO,CAAC;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACL,CAAC;AACH;AAEO,SAAS,UAAU,KAAY,OAAwB;AAG5D,MAAI,UAAU,MAAM,SAAS,IAAI,QAAQ;AACvC,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,IAAI,SAAS;AACzB,WAAS,IAAI,OAAO,IAAI,KAAK,KAAK;AAChC,QAAI,CAAC,IAAI,IAAI,IAAI,CAAC;AAAA,EACpB;AAEA,MAAI,SAAS;AACb,SAAO;AACT;AAEO,IAAM,WAAN,MAAM,UAAkB;AAAA,EAM7B,YAAY,SAAsB;AAChC,SAAK,UAAU,WAAW,IAAI,QAAW,CAAC,SAAS,WAAW;AAC5D,WAAK,UAAU;AACf,WAAK,SAAS;AAAA,IAChB,CAAC;AAAA,EACH;AAAA,EAEO,KAAK,aAAiC,YAAmC;AAC9E,WAAO,KAAK,QAAQ,KAAK,aAAa,UAAU;AAAA,EAClD;AAAA,EAEO,MAAM,MAA2B;AACtC,WAAO,KAAK,QAAQ,MAAM,IAAI;AAAA,EAChC;AAAA,EAEA,OAAO,OAAQ,QAAc;AAC3B,WAAO,IAAI,UAAS,QAAQ,OAAO,MAAM,CAAC;AAAA,EAC5C;AAAA,EAEA,OAAO,QAAiB,OAAW;AACjC,WAAO,IAAI,UAAY,QAAQ,QAAQ,KAAK,CAAC;AAAA,EAC/C;AAEF;AAEO,SAAS,MAAM,MAAW,MAAkB;AACjD,WAAS,IAAI,GAAG,MAAM,KAAK,QAAQ,IAAI,KAAK,KAAK;AAC/C,UAAM,IAAI,KAAK,CAAC;AAChB,eAAW,OAAO,GAAG;
|
|
4
|
+
"sourcesContent": ["import { nanoid } from 'nanoid';\nimport { type RoomException, type RoomMethodName } from '../errors/RoomExceptions.ts';\n\nimport { debugAndPrintError, debugMatchMaking } from '../Debug.ts';\n\nexport type Type<T> = new (...args: any[]) => T;\nexport type MethodName<T> = string & {\n [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never\n}[keyof T];\n\n/**\n * Utility type that extracts the return type of a method or the type of a property\n * from a given class/object type.\n *\n * - If the key is a method, returns the awaited return type of that method\n * - If the key is a property, returns the type of that property\n */\nexport type ExtractMethodOrPropertyType<\n TClass,\n TKey extends keyof TClass\n> = TClass[TKey] extends (...args: any[]) => infer R\n ? Awaited<R>\n : TClass[TKey];\n\n/**\n * Return type of `remoteRoomCall()`.\n *\n * Resolves to the method's awaited return type (or the property's type) when\n * the method name was captured as a literal type. Falls back to `any` when it\n * wasn't \u2014 e.g. `remoteRoomCall<MyRoom>(...)` with only the room type given:\n * TypeScript applies the `TMethod` default instead of inferring the literal\n * once an explicit type argument list is present (microsoft/TypeScript#26242).\n * Pass both type arguments (`remoteRoomCall<MyRoom, 'myMethod'>`) for a\n * precise return type.\n */\nexport type RemoteRoomCallReturn<\n TRoom,\n TMethod extends keyof TRoom\n> = keyof TRoom extends TMethod\n ? any\n : ExtractMethodOrPropertyType<TRoom, TMethod>;\n\n// remote room call timeouts\nexport const REMOTE_ROOM_SHORT_TIMEOUT = Number(process.env.COLYSEUS_PRESENCE_SHORT_TIMEOUT || 2000);\nexport const MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME = Number(process.env.COLYSEUS_MAX_CONCURRENT_CREATE_ROOM_WAIT_TIME || 0.5);\n\n// a peer that removes this process's room listings leaves a receipt. this\n// process looks for it every INTERVAL (ms) and restores them. the TTL (seconds)\n// only bounds what dead processes leave behind.\nexport const SWEPT_CHECK_INTERVAL = Number(process.env.COLYSEUS_SWEPT_CHECK_INTERVAL || 5000);\nexport const SWEPT_RECEIPT_TTL = Number(process.env.COLYSEUS_SWEPT_RECEIPT_TTL || 600);\n\nexport function generateId(length: number = 9) {\n return nanoid(length);\n}\n\nexport function getBearerToken(authHeader: string) {\n return (authHeader && authHeader.startsWith(\"Bearer \") && authHeader.substring(7, authHeader.length)) || undefined;\n}\n\n// nodemon sends SIGUSR2 before reloading\n// (https://github.com/remy/nodemon#controlling-shutdown-of-your-script)\n//\nconst signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGUSR2'];\n\nexport function registerGracefulShutdown(callback: (err?: Error) => void) {\n /**\n * Gracefully shutdown on uncaught errors\n */\n process.on('uncaughtException', (err) => {\n debugAndPrintError(err);\n callback(err);\n });\n\n signals.forEach((signal) =>\n process.once(signal, () => callback()));\n}\n\nexport function retry<T = any>(\n cb: Function,\n maxRetries: number = 3,\n errorWhiteList: any[] = [],\n retries: number = 0,\n) {\n return new Promise<T>((resolve, reject) => {\n cb()\n .then(resolve)\n .catch((e: any) => {\n if (\n errorWhiteList.indexOf(e.constructor) !== -1 &&\n retries++ < maxRetries\n ) {\n setTimeout(() => {\n debugMatchMaking(\"retrying due to error (error: %s, retries: %s, maxRetries: %s)\", e.message, retries, maxRetries);\n retry<T>(cb, maxRetries, errorWhiteList, retries).\n then(resolve).\n catch((e2) => reject(e2));\n }, Math.floor(Math.random() * Math.pow(2, retries) * 400));\n\n } else {\n reject(e);\n }\n });\n });\n}\n\nexport function spliceOne(arr: any[], index: number): boolean {\n // manually splice availableRooms array\n // http://jsperf.com/manual-splice\n if (index === -1 || index >= arr.length) {\n return false;\n }\n\n const len = arr.length - 1;\n for (let i = index; i < len; i++) {\n arr[i] = arr[i + 1];\n }\n\n arr.length = len;\n return true;\n}\n\nexport class Deferred<T = any> {\n public promise: Promise<T>;\n\n public resolve: Function;\n public reject: Function;\n\n constructor(promise?: Promise<T>) {\n this.promise = promise ?? new Promise<T>((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n\n public then(onFulfilled?: (value: T) => any, onRejected?: (reason: any) => any) {\n return this.promise.then(onFulfilled, onRejected);\n }\n\n public catch(func: (value: any) => any) {\n return this.promise.catch(func);\n }\n\n static reject (reason?: any) {\n return new Deferred(Promise.reject(reason));\n }\n\n static resolve<T = any>(value?: T) {\n return new Deferred<T>(Promise.resolve(value));\n }\n\n}\n\nexport function merge(a: any, ...objs: any[]): any {\n for (let i = 0, len = objs.length; i < len; i++) {\n const b = objs[i];\n for (const key in b) {\n // JSON.parse keeps \"__proto__\" as an own key: assigning it replaces a's prototype\n if (key !== \"__proto__\" && Object.hasOwn(b, key)) {\n a[key] = b[key];\n }\n }\n }\n return a;\n}\n\nexport function wrapTryCatch(\n method: Function,\n onError: (error: RoomException, methodName: RoomMethodName) => void,\n exceptionClass: Type<RoomException>,\n methodName: RoomMethodName,\n rethrow: boolean = false,\n ...additionalErrorArgs: any[]\n) {\n return (...args: any[]) => {\n try {\n const result = method(...args);\n if (typeof (result?.catch) === \"function\") {\n return result.catch((e: Error) => {\n onError(new exceptionClass(e, e.message, ...args, ...additionalErrorArgs), methodName);\n if (rethrow) { throw e; }\n });\n }\n return result;\n } catch (e: any) {\n onError(new exceptionClass(e, e.message, ...args, ...additionalErrorArgs), methodName);\n if (rethrow) { throw e; }\n }\n };\n}\n\n/**\n * Dynamically import a module using either require() or import()\n * based on the current module system (CJS vs ESM).\n *\n * This avoids double-loading packages when running in mixed ESM/CJS environments.\n * Errors are silently caught - await the promise and handle errors at usage site.\n */\nexport function dynamicImport<T = any>(moduleName: string): Promise<T> {\n // __dirname exists in CJS but not in ESM\n if (\n typeof __dirname !== 'undefined' &&\n // @ts-ignore\n typeof (Bun) === 'undefined' // prevent bun from loading CJS modules\n ) {\n // CJS context - use require()\n try {\n return Promise.resolve(require(moduleName));\n } catch (e: any) {\n // If the error is not a MODULE_NOT_FOUND error, reject with the error.\n if (e.code !== 'MODULE_NOT_FOUND') {\n return Promise.reject(e);\n }\n return Promise.resolve(undefined);\n }\n } else {\n // ESM context - use import()\n const promise = import(/* @vite-ignore */ moduleName);\n promise.catch(() => {}); // prevent unhandled rejection warnings\n return promise;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/build/utils/Utils.mjs
CHANGED