@colyseus/schema 3.0.0-alpha.27 → 3.0.0-alpha.29
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/cjs/index.js +114 -88
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +114 -88
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +114 -88
- package/lib/Metadata.d.ts +3 -0
- package/lib/Metadata.js.map +1 -1
- package/lib/Reflection.d.ts +1 -1
- package/lib/Reflection.js +4 -3
- package/lib/Reflection.js.map +1 -1
- package/lib/annotations.d.ts +0 -19
- package/lib/annotations.js +4 -106
- package/lib/annotations.js.map +1 -1
- package/lib/bench_encode.js +54 -27
- package/lib/bench_encode.js.map +1 -1
- package/lib/decoder/Decoder.d.ts +1 -1
- package/lib/decoder/Decoder.js +2 -2
- package/lib/decoder/Decoder.js.map +1 -1
- package/lib/decoder/strategy/StateCallbacks.js +5 -4
- package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
- package/lib/encoder/ChangeTree.d.ts +1 -12
- package/lib/encoder/ChangeTree.js +24 -51
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +6 -5
- package/lib/encoder/Encoder.js +28 -19
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +17 -0
- package/lib/encoder/Root.js +44 -0
- package/lib/encoder/Root.js.map +1 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.js +3 -3
- package/lib/index.js.map +1 -1
- package/lib/types/TypeContext.d.ts +23 -0
- package/lib/types/TypeContext.js +109 -0
- package/lib/types/TypeContext.js.map +1 -0
- package/lib/types/custom/ArraySchema.js +0 -1
- package/lib/types/custom/ArraySchema.js.map +1 -1
- package/package.json +1 -1
- package/src/Metadata.ts +1 -0
- package/src/Reflection.ts +2 -1
- package/src/annotations.ts +1 -126
- package/src/bench_encode.ts +47 -16
- package/src/decoder/Decoder.ts +1 -1
- package/src/decoder/strategy/StateCallbacks.ts +5 -4
- package/src/encoder/ChangeTree.ts +30 -59
- package/src/encoder/Encoder.ts +36 -23
- package/src/encoder/Root.ts +51 -0
- package/src/index.ts +3 -11
- package/src/types/TypeContext.ts +127 -0
- package/src/types/custom/ArraySchema.ts +0 -1
package/build/esm/index.mjs
CHANGED
|
@@ -194,44 +194,6 @@ const Metadata = {
|
|
|
194
194
|
};
|
|
195
195
|
|
|
196
196
|
var _a$5;
|
|
197
|
-
class Root {
|
|
198
|
-
constructor() {
|
|
199
|
-
this.nextUniqueId = 0;
|
|
200
|
-
this.refCount = new WeakMap();
|
|
201
|
-
// all changes
|
|
202
|
-
this.allChanges = new Map();
|
|
203
|
-
this.allFilteredChanges = new Map();
|
|
204
|
-
// pending changes to be encoded
|
|
205
|
-
this.changes = new Map();
|
|
206
|
-
this.filteredChanges = new Map();
|
|
207
|
-
}
|
|
208
|
-
getNextUniqueId() {
|
|
209
|
-
return this.nextUniqueId++;
|
|
210
|
-
}
|
|
211
|
-
add(changeTree) {
|
|
212
|
-
const refCount = this.refCount.get(changeTree) || 0;
|
|
213
|
-
this.refCount.set(changeTree, refCount + 1);
|
|
214
|
-
}
|
|
215
|
-
remove(changeTree) {
|
|
216
|
-
const refCount = this.refCount.get(changeTree);
|
|
217
|
-
if (refCount <= 1) {
|
|
218
|
-
this.allChanges.delete(changeTree);
|
|
219
|
-
this.changes.delete(changeTree);
|
|
220
|
-
if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
|
|
221
|
-
this.allFilteredChanges.delete(changeTree);
|
|
222
|
-
this.filteredChanges.delete(changeTree);
|
|
223
|
-
}
|
|
224
|
-
this.refCount.delete(changeTree);
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
this.refCount.set(changeTree, refCount - 1);
|
|
228
|
-
}
|
|
229
|
-
changeTree.forEachChild((child, _) => this.remove(child));
|
|
230
|
-
}
|
|
231
|
-
clear() {
|
|
232
|
-
this.changes.clear();
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
197
|
class ChangeTree {
|
|
236
198
|
static { _a$5 = $isNew; }
|
|
237
199
|
;
|
|
@@ -263,8 +225,6 @@ class ChangeTree {
|
|
|
263
225
|
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
264
226
|
this.root.allFilteredChanges.set(this, this.allFilteredChanges);
|
|
265
227
|
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
266
|
-
// } else {
|
|
267
|
-
// this.root.allChanges.set(this, this.allChanges);
|
|
268
228
|
}
|
|
269
229
|
if (!this.isFiltered) {
|
|
270
230
|
this.root.allChanges.set(this, this.allChanges);
|
|
@@ -537,15 +497,30 @@ class ChangeTree {
|
|
|
537
497
|
checkIsFiltered(parent, parentIndex) {
|
|
538
498
|
// Detect if current structure has "filters" declared
|
|
539
499
|
this.isPartiallyFiltered = (this.ref['constructor']?.[Symbol.metadata]?.[-2] !== undefined);
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
500
|
+
if (parent && !Metadata.isValidInstance(parent)) {
|
|
501
|
+
const parentChangeTree = parent[$changes];
|
|
502
|
+
parent = parentChangeTree.parent;
|
|
503
|
+
parentIndex = parentChangeTree.parentIndex;
|
|
504
|
+
}
|
|
505
|
+
const parentMetadata = parent?.['constructor']?.[Symbol.metadata];
|
|
506
|
+
this.isFiltered = (parent &&
|
|
507
|
+
parentMetadata?.[-2]?.includes(parentIndex));
|
|
508
|
+
// this.isFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-4];
|
|
509
|
+
// // Detect if parent has "filters" declared
|
|
510
|
+
// while (parent && !this.isFiltered) {
|
|
511
|
+
// const metadata: Metadata = parent['constructor'][Symbol.metadata];
|
|
512
|
+
// // this.isFiltered = metadata?.[-4];
|
|
513
|
+
// const fieldName = metadata?.[parentIndex];
|
|
514
|
+
// const isParentOwned = metadata?.[fieldName]?.tag !== undefined;
|
|
515
|
+
// this.isFiltered = isParentOwned || parent[$changes].isFiltered; // metadata?.[-2]
|
|
516
|
+
// parent = parent[$changes].parent;
|
|
517
|
+
// };
|
|
518
|
+
// console.log("ChangeTree.checkIsFiltered", {
|
|
519
|
+
// parent: parent?.constructor.name,
|
|
520
|
+
// ref: this.ref.constructor.name,
|
|
521
|
+
// isFiltered: this.isFiltered,
|
|
522
|
+
// isPartiallyFiltered: this.isPartiallyFiltered,
|
|
523
|
+
// });
|
|
549
524
|
//
|
|
550
525
|
// TODO: refactor this!
|
|
551
526
|
//
|
|
@@ -1686,7 +1661,6 @@ class ArraySchema {
|
|
|
1686
1661
|
}
|
|
1687
1662
|
const changeTree = this[$changes];
|
|
1688
1663
|
changeTree.indexedOperation(length, OPERATION.ADD, this.items.length);
|
|
1689
|
-
// changeTree.indexes[length] = length;
|
|
1690
1664
|
this.items.push(value);
|
|
1691
1665
|
this.tmpItems.push(value);
|
|
1692
1666
|
//
|
|
@@ -2377,7 +2351,6 @@ class MapSchema {
|
|
|
2377
2351
|
}
|
|
2378
2352
|
registerType("map", { constructor: MapSchema });
|
|
2379
2353
|
|
|
2380
|
-
const DEFAULT_VIEW_TAG = -1;
|
|
2381
2354
|
class TypeContext {
|
|
2382
2355
|
/**
|
|
2383
2356
|
* For inheritance support
|
|
@@ -2399,6 +2372,7 @@ class TypeContext {
|
|
|
2399
2372
|
this.types = {};
|
|
2400
2373
|
this.schemas = new Map();
|
|
2401
2374
|
this.hasFilters = false;
|
|
2375
|
+
this.parentFiltered = {};
|
|
2402
2376
|
if (rootClass) {
|
|
2403
2377
|
this.discoverTypes(rootClass);
|
|
2404
2378
|
}
|
|
@@ -2427,32 +2401,32 @@ class TypeContext {
|
|
|
2427
2401
|
getTypeId(klass) {
|
|
2428
2402
|
return this.schemas.get(klass);
|
|
2429
2403
|
}
|
|
2430
|
-
discoverTypes(klass, parentFieldViewTag) {
|
|
2404
|
+
discoverTypes(klass, parentIndex, parentFieldViewTag) {
|
|
2431
2405
|
if (!this.add(klass)) {
|
|
2432
2406
|
return;
|
|
2433
2407
|
}
|
|
2434
2408
|
// add classes inherited from this base class
|
|
2435
2409
|
TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
|
|
2436
|
-
this.discoverTypes(child, parentFieldViewTag);
|
|
2410
|
+
this.discoverTypes(child, parentIndex, parentFieldViewTag);
|
|
2437
2411
|
});
|
|
2438
|
-
|
|
2439
|
-
if (klass[Symbol.metadata] === undefined) {
|
|
2440
|
-
klass[Symbol.metadata] = {};
|
|
2441
|
-
}
|
|
2442
|
-
// const metadata = Metadata.getFor(klass);
|
|
2443
|
-
const metadata = klass[Symbol.metadata];
|
|
2412
|
+
const metadata = (klass[Symbol.metadata] ??= {});
|
|
2444
2413
|
// if any schema/field has filters, mark "context" as having filters.
|
|
2445
2414
|
if (metadata[-2]) {
|
|
2446
2415
|
this.hasFilters = true;
|
|
2447
2416
|
}
|
|
2417
|
+
if (parentFieldViewTag !== undefined) {
|
|
2418
|
+
this.parentFiltered[`${this.schemas.get(klass)}-${parentIndex}`] = true;
|
|
2419
|
+
}
|
|
2448
2420
|
for (const field in metadata) {
|
|
2449
|
-
//
|
|
2450
|
-
// Modify the field's metadata to include the parent field's view tag
|
|
2451
|
-
//
|
|
2452
|
-
if (
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2421
|
+
// //
|
|
2422
|
+
// // Modify the field's metadata to include the parent field's view tag
|
|
2423
|
+
// //
|
|
2424
|
+
// if (
|
|
2425
|
+
// parentFieldViewTag !== undefined &&
|
|
2426
|
+
// metadata[field].tag === undefined
|
|
2427
|
+
// ) {
|
|
2428
|
+
// metadata[field].tag = parentFieldViewTag;
|
|
2429
|
+
// }
|
|
2456
2430
|
const fieldType = metadata[field].type;
|
|
2457
2431
|
const viewTag = metadata[field].tag;
|
|
2458
2432
|
if (typeof (fieldType) === "string") {
|
|
@@ -2463,7 +2437,7 @@ class TypeContext {
|
|
|
2463
2437
|
if (type === "string") {
|
|
2464
2438
|
continue;
|
|
2465
2439
|
}
|
|
2466
|
-
this.discoverTypes(type, viewTag);
|
|
2440
|
+
this.discoverTypes(type, metadata[field].index, viewTag);
|
|
2467
2441
|
}
|
|
2468
2442
|
else if (typeof (fieldType) === "function") {
|
|
2469
2443
|
this.discoverTypes(fieldType, viewTag);
|
|
@@ -2474,11 +2448,13 @@ class TypeContext {
|
|
|
2474
2448
|
if (typeof (type) === "string") {
|
|
2475
2449
|
continue;
|
|
2476
2450
|
}
|
|
2477
|
-
this.discoverTypes(type, viewTag);
|
|
2451
|
+
this.discoverTypes(type, metadata[field].index, viewTag);
|
|
2478
2452
|
}
|
|
2479
2453
|
}
|
|
2480
2454
|
}
|
|
2481
2455
|
}
|
|
2456
|
+
|
|
2457
|
+
const DEFAULT_VIEW_TAG = -1;
|
|
2482
2458
|
/**
|
|
2483
2459
|
* [See documentation](https://docs.colyseus.io/state/schema/)
|
|
2484
2460
|
*
|
|
@@ -3440,16 +3416,56 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
3440
3416
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
3441
3417
|
};
|
|
3442
3418
|
|
|
3419
|
+
class Root {
|
|
3420
|
+
constructor(types) {
|
|
3421
|
+
this.types = types;
|
|
3422
|
+
this.nextUniqueId = 0;
|
|
3423
|
+
this.refCount = new WeakMap();
|
|
3424
|
+
// all changes
|
|
3425
|
+
this.allChanges = new Map();
|
|
3426
|
+
this.allFilteredChanges = new Map();
|
|
3427
|
+
// pending changes to be encoded
|
|
3428
|
+
this.changes = new Map();
|
|
3429
|
+
this.filteredChanges = new Map();
|
|
3430
|
+
}
|
|
3431
|
+
getNextUniqueId() {
|
|
3432
|
+
return this.nextUniqueId++;
|
|
3433
|
+
}
|
|
3434
|
+
add(changeTree) {
|
|
3435
|
+
const refCount = this.refCount.get(changeTree) || 0;
|
|
3436
|
+
this.refCount.set(changeTree, refCount + 1);
|
|
3437
|
+
}
|
|
3438
|
+
remove(changeTree) {
|
|
3439
|
+
const refCount = this.refCount.get(changeTree);
|
|
3440
|
+
if (refCount <= 1) {
|
|
3441
|
+
this.allChanges.delete(changeTree);
|
|
3442
|
+
this.changes.delete(changeTree);
|
|
3443
|
+
if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
|
|
3444
|
+
this.allFilteredChanges.delete(changeTree);
|
|
3445
|
+
this.filteredChanges.delete(changeTree);
|
|
3446
|
+
}
|
|
3447
|
+
this.refCount.delete(changeTree);
|
|
3448
|
+
}
|
|
3449
|
+
else {
|
|
3450
|
+
this.refCount.set(changeTree, refCount - 1);
|
|
3451
|
+
}
|
|
3452
|
+
changeTree.forEachChild((child, _) => this.remove(child));
|
|
3453
|
+
}
|
|
3454
|
+
clear() {
|
|
3455
|
+
this.changes.clear();
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
|
|
3443
3459
|
class Encoder {
|
|
3444
3460
|
static { this.BUFFER_SIZE = 8 * 1024; } // 8KB
|
|
3445
3461
|
constructor(state) {
|
|
3446
3462
|
this.sharedBuffer = Buffer.allocUnsafeSlow(Encoder.BUFFER_SIZE);
|
|
3447
|
-
this.root = new Root();
|
|
3448
3463
|
//
|
|
3449
3464
|
// TODO: cache and restore "Context" based on root schema
|
|
3450
3465
|
// (to avoid creating a new context for every new room)
|
|
3451
3466
|
//
|
|
3452
3467
|
this.context = new TypeContext(state.constructor);
|
|
3468
|
+
this.root = new Root(this.context);
|
|
3453
3469
|
this.setState(state);
|
|
3454
3470
|
// console.log(">>>>>>>>>>>>>>>> Encoder types");
|
|
3455
3471
|
// this.context.schemas.forEach((id, schema) => {
|
|
@@ -3486,7 +3502,8 @@ class Encoder {
|
|
|
3486
3502
|
}
|
|
3487
3503
|
}
|
|
3488
3504
|
// skip root `refId` if it's the first change tree
|
|
3489
|
-
|
|
3505
|
+
// (unless it "hasView", which will need to revisit the root)
|
|
3506
|
+
if (hasView || changeTree !== rootChangeTree) {
|
|
3490
3507
|
buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
|
|
3491
3508
|
number$1(buffer, changeTree.refId, it);
|
|
3492
3509
|
}
|
|
@@ -3548,35 +3565,43 @@ class Encoder {
|
|
|
3548
3565
|
}
|
|
3549
3566
|
}
|
|
3550
3567
|
encodeAll(it = { offset: 0 }, buffer = this.sharedBuffer) {
|
|
3551
|
-
// console.log(
|
|
3552
|
-
//
|
|
3553
|
-
// console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
|
|
3554
|
-
// });
|
|
3568
|
+
// console.log(`\nencodeAll(), this.root.allChanges (${this.root.allChanges.size})`);
|
|
3569
|
+
// this.debugChanges("allChanges");
|
|
3555
3570
|
return this.encode(it, undefined, buffer, this.root.allChanges, true);
|
|
3556
3571
|
}
|
|
3557
3572
|
encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3558
3573
|
const viewOffset = it.offset;
|
|
3559
|
-
// console.log(
|
|
3560
|
-
// this.
|
|
3574
|
+
// console.log(`\nencodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
|
|
3575
|
+
// this.debugChanges("allFilteredChanges");
|
|
3561
3576
|
// try to encode "filtered" changes
|
|
3562
|
-
this.encode(it, view, bytes, this.root.allFilteredChanges, true);
|
|
3577
|
+
this.encode(it, view, bytes, this.root.allFilteredChanges, true, viewOffset);
|
|
3563
3578
|
return Buffer.concat([
|
|
3564
3579
|
bytes.subarray(0, sharedOffset),
|
|
3565
3580
|
bytes.subarray(viewOffset, it.offset)
|
|
3566
3581
|
]);
|
|
3567
3582
|
}
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3583
|
+
debugChanges(field) {
|
|
3584
|
+
const changeSet = (typeof (field) === "string")
|
|
3585
|
+
? this.root[field]
|
|
3586
|
+
: field;
|
|
3587
|
+
Array.from(changeSet.entries()).map((item) => {
|
|
3588
|
+
const metadata = item[0].ref.constructor[Symbol.metadata];
|
|
3589
|
+
console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
|
|
3590
|
+
item[1].forEach((op, index) => {
|
|
3591
|
+
console.log(" ->", {
|
|
3592
|
+
index,
|
|
3593
|
+
field: metadata?.[index],
|
|
3594
|
+
op: OPERATION[op],
|
|
3574
3595
|
});
|
|
3575
|
-
}
|
|
3596
|
+
});
|
|
3576
3597
|
});
|
|
3577
3598
|
}
|
|
3578
3599
|
encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3579
3600
|
const viewOffset = it.offset;
|
|
3601
|
+
// console.log(`\nencodeView(), view.changes (${view.changes.size})`);
|
|
3602
|
+
// this.debugChanges(view.changes);
|
|
3603
|
+
// console.log(`\nencodeView(), this.root.filteredChanges (${this.root.filteredChanges.size})`);
|
|
3604
|
+
// this.debugChanges("filteredChanges");
|
|
3580
3605
|
// encode visibility changes (add/remove for this view)
|
|
3581
3606
|
const viewChangesIterator = view.changes.entries();
|
|
3582
3607
|
for (const [changeTree, changes] of viewChangesIterator) {
|
|
@@ -4131,11 +4156,11 @@ function getDecoderStateCallbacks(decoder) {
|
|
|
4131
4156
|
let isCollection = ((context.instance && typeof (context.instance['forEach']) === "function") ||
|
|
4132
4157
|
(metadataOrType && typeof (metadataOrType[Symbol.metadata]) === "undefined"));
|
|
4133
4158
|
if (metadata && !isCollection) {
|
|
4134
|
-
const
|
|
4159
|
+
const onAddListen = function (ref, prop, callback, immediate) {
|
|
4135
4160
|
// immediate trigger
|
|
4136
4161
|
if (immediate &&
|
|
4137
4162
|
context.instance[prop] !== undefined &&
|
|
4138
|
-
!onAddCalls.has(
|
|
4163
|
+
!onAddCalls.has(currentOnAddCallback) // Workaround for https://github.com/colyseus/schema/issues/147
|
|
4139
4164
|
) {
|
|
4140
4165
|
callback(context.instance[prop], undefined);
|
|
4141
4166
|
}
|
|
@@ -4147,13 +4172,13 @@ function getDecoderStateCallbacks(decoder) {
|
|
|
4147
4172
|
return new Proxy({
|
|
4148
4173
|
listen: function listen(prop, callback, immediate = true) {
|
|
4149
4174
|
if (context.instance) {
|
|
4150
|
-
return
|
|
4175
|
+
return onAddListen(context.instance, prop, callback, immediate);
|
|
4151
4176
|
}
|
|
4152
4177
|
else {
|
|
4153
4178
|
// collection instance not received yet
|
|
4154
4179
|
let detachCallback = () => { };
|
|
4155
4180
|
context.onInstanceAvailable((ref, existing) => {
|
|
4156
|
-
detachCallback =
|
|
4181
|
+
detachCallback = onAddListen(ref, prop, callback, immediate && existing && !onAddCalls.has(currentOnAddCallback));
|
|
4157
4182
|
});
|
|
4158
4183
|
return () => detachCallback();
|
|
4159
4184
|
}
|
|
@@ -4222,6 +4247,7 @@ function getDecoderStateCallbacks(decoder) {
|
|
|
4222
4247
|
currentOnAddCallback = callback;
|
|
4223
4248
|
callback(value, key);
|
|
4224
4249
|
onAddCalls.delete(callback);
|
|
4250
|
+
currentOnAddCallback = undefined;
|
|
4225
4251
|
});
|
|
4226
4252
|
};
|
|
4227
4253
|
const onRemove = function (ref, callback) {
|