@colyseus/schema 3.0.0-alpha.25 → 3.0.0-alpha.26
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 +77 -85
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +77 -85
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +77 -85
- package/lib/Reflection.js +1 -3
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.d.ts +1 -1
- package/lib/Schema.js +2 -2
- package/lib/Schema.js.map +1 -1
- package/lib/annotations.js +13 -5
- package/lib/annotations.js.map +1 -1
- package/lib/decoder/DecodeOperation.js +1 -0
- package/lib/decoder/DecodeOperation.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/encoder/ChangeTree.js +3 -2
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +3 -3
- package/lib/encoder/Encoder.js +10 -11
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/StateView.d.ts +2 -2
- package/lib/encoder/StateView.js +45 -60
- package/lib/encoder/StateView.js.map +1 -1
- package/package.json +1 -1
- package/src/Reflection.ts +1 -3
- package/src/Schema.ts +2 -2
- package/src/annotations.ts +16 -5
- package/src/decoder/DecodeOperation.ts +4 -1
- package/src/decoder/Decoder.ts +3 -2
- package/src/encoder/ChangeTree.ts +4 -2
- package/src/encoder/Encoder.ts +11 -13
- package/src/encoder/StateView.ts +50 -69
package/build/esm/index.mjs
CHANGED
|
@@ -327,7 +327,7 @@ class ChangeTree {
|
|
|
327
327
|
// MapSchema / ArraySchema, etc.
|
|
328
328
|
this.ref.forEach((value, key) => {
|
|
329
329
|
if (Metadata.isValidInstance(value)) {
|
|
330
|
-
callback(value[$changes], this.ref[$changes].indexes[key]);
|
|
330
|
+
callback(value[$changes], this.ref[$changes].indexes[key] ?? key);
|
|
331
331
|
}
|
|
332
332
|
});
|
|
333
333
|
}
|
|
@@ -355,8 +355,9 @@ class ChangeTree {
|
|
|
355
355
|
// TODO: are DELETE operations being encoded as ADD here ??
|
|
356
356
|
//
|
|
357
357
|
if (isFiltered) {
|
|
358
|
-
this.allFilteredChanges.set(index, OPERATION.ADD);
|
|
359
358
|
this.root?.filteredChanges.set(this, this.filteredChanges);
|
|
359
|
+
this.allFilteredChanges.set(index, OPERATION.ADD);
|
|
360
|
+
this.root?.allFilteredChanges.set(this, this.allFilteredChanges);
|
|
360
361
|
}
|
|
361
362
|
else {
|
|
362
363
|
this.allChanges.set(index, OPERATION.ADD);
|
|
@@ -1403,6 +1404,7 @@ const decodeSchemaOperation = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1403
1404
|
// skip early if field is not defined
|
|
1404
1405
|
const field = metadata[index];
|
|
1405
1406
|
if (field === undefined) {
|
|
1407
|
+
console.warn("@colyseus/schema: field not defined at", { index, ref: ref.constructor.name, metadata });
|
|
1406
1408
|
return DEFINITION_MISMATCH;
|
|
1407
1409
|
}
|
|
1408
1410
|
const { value, previousValue } = decodeValue(decoder, operation, ref, index, metadata[field].type, bytes, it, allChanges);
|
|
@@ -2425,13 +2427,13 @@ class TypeContext {
|
|
|
2425
2427
|
getTypeId(klass) {
|
|
2426
2428
|
return this.schemas.get(klass);
|
|
2427
2429
|
}
|
|
2428
|
-
discoverTypes(klass) {
|
|
2430
|
+
discoverTypes(klass, parentFieldViewTag) {
|
|
2429
2431
|
if (!this.add(klass)) {
|
|
2430
2432
|
return;
|
|
2431
2433
|
}
|
|
2432
2434
|
// add classes inherited from this base class
|
|
2433
2435
|
TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
|
|
2434
|
-
this.discoverTypes(child);
|
|
2436
|
+
this.discoverTypes(child, parentFieldViewTag);
|
|
2435
2437
|
});
|
|
2436
2438
|
// skip if no fields are defined for this class.
|
|
2437
2439
|
if (klass[Symbol.metadata] === undefined) {
|
|
@@ -2444,7 +2446,15 @@ class TypeContext {
|
|
|
2444
2446
|
this.hasFilters = true;
|
|
2445
2447
|
}
|
|
2446
2448
|
for (const field in metadata) {
|
|
2449
|
+
//
|
|
2450
|
+
// Modify the field's metadata to include the parent field's view tag
|
|
2451
|
+
//
|
|
2452
|
+
if (parentFieldViewTag !== undefined &&
|
|
2453
|
+
metadata[field].tag === undefined) {
|
|
2454
|
+
metadata[field].tag = parentFieldViewTag;
|
|
2455
|
+
}
|
|
2447
2456
|
const fieldType = metadata[field].type;
|
|
2457
|
+
const viewTag = metadata[field].tag;
|
|
2448
2458
|
if (typeof (fieldType) === "string") {
|
|
2449
2459
|
continue;
|
|
2450
2460
|
}
|
|
@@ -2453,10 +2463,10 @@ class TypeContext {
|
|
|
2453
2463
|
if (type === "string") {
|
|
2454
2464
|
continue;
|
|
2455
2465
|
}
|
|
2456
|
-
this.discoverTypes(type);
|
|
2466
|
+
this.discoverTypes(type, viewTag);
|
|
2457
2467
|
}
|
|
2458
2468
|
else if (typeof (fieldType) === "function") {
|
|
2459
|
-
this.discoverTypes(fieldType);
|
|
2469
|
+
this.discoverTypes(fieldType, viewTag);
|
|
2460
2470
|
}
|
|
2461
2471
|
else {
|
|
2462
2472
|
const type = Object.values(fieldType)[0];
|
|
@@ -2464,7 +2474,7 @@ class TypeContext {
|
|
|
2464
2474
|
if (typeof (type) === "string") {
|
|
2465
2475
|
continue;
|
|
2466
2476
|
}
|
|
2467
|
-
this.discoverTypes(type);
|
|
2477
|
+
this.discoverTypes(type, viewTag);
|
|
2468
2478
|
}
|
|
2469
2479
|
}
|
|
2470
2480
|
}
|
|
@@ -3023,13 +3033,13 @@ class Schema {
|
|
|
3023
3033
|
}
|
|
3024
3034
|
return output;
|
|
3025
3035
|
}
|
|
3026
|
-
static debugChangesDeep(ref) {
|
|
3036
|
+
static debugChangesDeep(ref, changeSetName = "changes") {
|
|
3027
3037
|
let output = "";
|
|
3028
3038
|
const rootChangeTree = ref[$changes];
|
|
3029
3039
|
const changeTrees = new Map();
|
|
3030
3040
|
let totalInstances = 0;
|
|
3031
3041
|
let totalOperations = 0;
|
|
3032
|
-
for (const [changeTree, changes] of (rootChangeTree.root.
|
|
3042
|
+
for (const [changeTree, changes] of (rootChangeTree.root[changeSetName].entries())) {
|
|
3033
3043
|
let includeChangeTree = false;
|
|
3034
3044
|
let parentChangeTrees = [];
|
|
3035
3045
|
let parentChangeTree = changeTree.parent?.[$changes];
|
|
@@ -3432,26 +3442,26 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
3432
3442
|
|
|
3433
3443
|
class Encoder {
|
|
3434
3444
|
static { this.BUFFER_SIZE = 8 * 1024; } // 8KB
|
|
3435
|
-
constructor(
|
|
3445
|
+
constructor(state) {
|
|
3436
3446
|
this.sharedBuffer = Buffer.allocUnsafeSlow(Encoder.BUFFER_SIZE);
|
|
3447
|
+
this.root = new Root();
|
|
3437
3448
|
//
|
|
3438
3449
|
// TODO: cache and restore "Context" based on root schema
|
|
3439
3450
|
// (to avoid creating a new context for every new room)
|
|
3440
3451
|
//
|
|
3441
|
-
this.context = new TypeContext(
|
|
3442
|
-
this.
|
|
3452
|
+
this.context = new TypeContext(state.constructor);
|
|
3453
|
+
this.setState(state);
|
|
3443
3454
|
// console.log(">>>>>>>>>>>>>>>> Encoder types");
|
|
3444
3455
|
// this.context.schemas.forEach((id, schema) => {
|
|
3445
3456
|
// console.log("type:", id, schema.name, Object.keys(schema[Symbol.metadata]));
|
|
3446
3457
|
// });
|
|
3447
3458
|
}
|
|
3448
|
-
|
|
3449
|
-
this.root = new Root();
|
|
3459
|
+
setState(state) {
|
|
3450
3460
|
this.state = state;
|
|
3451
|
-
state[$changes].setRoot(this.root);
|
|
3461
|
+
this.state[$changes].setRoot(this.root);
|
|
3452
3462
|
}
|
|
3453
|
-
encode(it = { offset: 0 }, view, buffer = this.sharedBuffer, changeTrees = this.root.changes, isEncodeAll = this.root.allChanges === changeTrees
|
|
3454
|
-
|
|
3463
|
+
encode(it = { offset: 0 }, view, buffer = this.sharedBuffer, changeTrees = this.root.changes, isEncodeAll = this.root.allChanges === changeTrees, initialOffset = it.offset // cache current offset in case we need to resize the buffer
|
|
3464
|
+
) {
|
|
3455
3465
|
const hasView = (view !== undefined);
|
|
3456
3466
|
const rootChangeTree = this.state[$changes];
|
|
3457
3467
|
const changeTreesIterator = changeTrees.entries();
|
|
@@ -3490,7 +3500,6 @@ class Encoder {
|
|
|
3490
3500
|
// TODO: avoid checking if no view tags were defined
|
|
3491
3501
|
//
|
|
3492
3502
|
if (filter && !filter(ref, fieldIndex, view)) {
|
|
3493
|
-
// console.log("SKIP FIELD:", { ref: changeTree.ref.constructor.name, fieldIndex, })
|
|
3494
3503
|
// console.log("ADD AS INVISIBLE:", fieldIndex, changeTree.ref.constructor.name)
|
|
3495
3504
|
// view?.invisible.add(changeTree);
|
|
3496
3505
|
continue;
|
|
@@ -3568,8 +3577,6 @@ class Encoder {
|
|
|
3568
3577
|
}
|
|
3569
3578
|
encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3570
3579
|
const viewOffset = it.offset;
|
|
3571
|
-
// try to encode "filtered" changes
|
|
3572
|
-
this.encode(it, view, bytes, this.root.filteredChanges);
|
|
3573
3580
|
// encode visibility changes (add/remove for this view)
|
|
3574
3581
|
const viewChangesIterator = view.changes.entries();
|
|
3575
3582
|
for (const [changeTree, changes] of viewChangesIterator) {
|
|
@@ -3596,6 +3603,8 @@ class Encoder {
|
|
|
3596
3603
|
//
|
|
3597
3604
|
// clear "view" changes after encoding
|
|
3598
3605
|
view.changes.clear();
|
|
3606
|
+
// try to encode "filtered" changes
|
|
3607
|
+
this.encode(it, view, bytes, this.root.filteredChanges, false, viewOffset);
|
|
3599
3608
|
return Buffer.concat([
|
|
3600
3609
|
bytes.subarray(0, sharedOffset),
|
|
3601
3610
|
bytes.subarray(viewOffset, it.offset)
|
|
@@ -3768,14 +3777,14 @@ class ReferenceTracker {
|
|
|
3768
3777
|
class Decoder {
|
|
3769
3778
|
constructor(root, context) {
|
|
3770
3779
|
this.currentRefId = 0;
|
|
3771
|
-
this.
|
|
3780
|
+
this.setState(root);
|
|
3772
3781
|
this.context = context || new TypeContext(root.constructor);
|
|
3773
3782
|
// console.log(">>>>>>>>>>>>>>>> Decoder types");
|
|
3774
3783
|
// this.context.schemas.forEach((id, schema) => {
|
|
3775
3784
|
// console.log("type:", id, schema.name, Object.keys(schema[Symbol.metadata]));
|
|
3776
3785
|
// });
|
|
3777
3786
|
}
|
|
3778
|
-
|
|
3787
|
+
setState(root) {
|
|
3779
3788
|
this.state = root;
|
|
3780
3789
|
this.root = new ReferenceTracker();
|
|
3781
3790
|
this.root.addRef(0, root);
|
|
@@ -3904,9 +3913,7 @@ class Reflection extends Schema {
|
|
|
3904
3913
|
this.types = new ArraySchema();
|
|
3905
3914
|
}
|
|
3906
3915
|
static encode(instance, context, it = { offset: 0 }) {
|
|
3907
|
-
|
|
3908
|
-
context = new TypeContext(instance.constructor);
|
|
3909
|
-
}
|
|
3916
|
+
context ??= new TypeContext(instance.constructor);
|
|
3910
3917
|
const reflection = new Reflection();
|
|
3911
3918
|
const encoder = new Encoder(reflection);
|
|
3912
3919
|
const buildType = (currentType, metadata) => {
|
|
@@ -4288,26 +4295,21 @@ class StateView {
|
|
|
4288
4295
|
this.changes = new Map();
|
|
4289
4296
|
}
|
|
4290
4297
|
// TODO: allow to set multiple tags at once
|
|
4291
|
-
add(obj, tag = DEFAULT_VIEW_TAG) {
|
|
4298
|
+
add(obj, tag = DEFAULT_VIEW_TAG, checkIncludeParent = true) {
|
|
4292
4299
|
if (!obj[$changes]) {
|
|
4293
4300
|
console.warn("StateView#add(), invalid object:", obj);
|
|
4294
4301
|
return this;
|
|
4295
4302
|
}
|
|
4296
4303
|
// FIXME: ArraySchema/MapSchema does not have metadata
|
|
4297
4304
|
const metadata = obj.constructor[Symbol.metadata];
|
|
4298
|
-
|
|
4305
|
+
const changeTree = obj[$changes];
|
|
4299
4306
|
this.items.add(changeTree);
|
|
4300
|
-
//
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
this.add(change.ref, tag);
|
|
4307
|
-
});
|
|
4308
|
-
// add parent ChangeTree's, if they are invisible to this view
|
|
4309
|
-
// TODO: REFACTOR addParent()
|
|
4310
|
-
this.addParent(changeTree, tag);
|
|
4307
|
+
// add parent ChangeTree's
|
|
4308
|
+
// - if it was invisible to this view
|
|
4309
|
+
// - if it were previously filtered out
|
|
4310
|
+
if (checkIncludeParent && changeTree.parent) {
|
|
4311
|
+
this.addParent(changeTree.parent[$changes], changeTree.parentIndex, tag);
|
|
4312
|
+
}
|
|
4311
4313
|
//
|
|
4312
4314
|
// TODO: when adding an item of a MapSchema, the changes may not
|
|
4313
4315
|
// be set (only the parent's changes are set)
|
|
@@ -4339,73 +4341,63 @@ class StateView {
|
|
|
4339
4341
|
});
|
|
4340
4342
|
}
|
|
4341
4343
|
else {
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
// metadata?.[-3]?.[DEFAULT_VIEW_TAG]?.forEach((index) => {
|
|
4345
|
-
// if (changeTree.getChange(index) !== OPERATION.DELETE) {
|
|
4346
|
-
// changes.set(index, OPERATION.ADD);
|
|
4347
|
-
// }
|
|
4348
|
-
// });
|
|
4349
|
-
const allChangesSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
|
|
4344
|
+
const isInvisible = this.invisible.has(changeTree);
|
|
4345
|
+
const changeSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
|
|
4350
4346
|
? changeTree.allFilteredChanges
|
|
4351
4347
|
: changeTree.allChanges;
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4348
|
+
changeSet.forEach((op, index) => {
|
|
4349
|
+
const tagAtIndex = metadata?.[metadata?.[index]].tag;
|
|
4350
|
+
if ((isInvisible || // if "invisible", include all
|
|
4351
|
+
tagAtIndex === undefined || // "all change" with no tag
|
|
4352
|
+
tagAtIndex === tag // tagged property
|
|
4353
|
+
) &&
|
|
4354
|
+
op !== OPERATION.DELETE) {
|
|
4355
|
+
changes.set(index, op);
|
|
4358
4356
|
}
|
|
4359
|
-
}
|
|
4360
|
-
}
|
|
4361
|
-
// TODO: avoid unnecessary iteration here
|
|
4362
|
-
while (changeTree.parent &&
|
|
4363
|
-
(changeTree = changeTree.parent[$changes]) &&
|
|
4364
|
-
(changeTree.isFiltered || changeTree.isPartiallyFiltered)) {
|
|
4365
|
-
this.items.add(changeTree);
|
|
4357
|
+
});
|
|
4366
4358
|
}
|
|
4359
|
+
// Add children of this ChangeTree to this view
|
|
4360
|
+
changeTree.forEachChild((change, index) => {
|
|
4361
|
+
// Do not ADD children that don't have the same tag
|
|
4362
|
+
if (metadata && metadata[metadata[index]].tag !== tag) {
|
|
4363
|
+
return;
|
|
4364
|
+
}
|
|
4365
|
+
this.add(change.ref, tag, false);
|
|
4366
|
+
});
|
|
4367
4367
|
return this;
|
|
4368
4368
|
}
|
|
4369
|
-
addParent(changeTree, tag) {
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4369
|
+
addParent(changeTree, parentIndex, tag) {
|
|
4370
|
+
// view must have all "changeTree" parent tree
|
|
4371
|
+
this.items.add(changeTree);
|
|
4372
|
+
// add parent's parent
|
|
4373
|
+
const parentChangeTree = changeTree.parent?.[$changes];
|
|
4374
|
+
if (parentChangeTree && (parentChangeTree.isFiltered || parentChangeTree.isPartiallyFiltered)) {
|
|
4375
|
+
this.addParent(parentChangeTree, changeTree.parentIndex, tag);
|
|
4373
4376
|
}
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
if (!this.invisible.has(parentChangeTree)) {
|
|
4377
|
-
// parent is already available, no need to add it!
|
|
4377
|
+
// parent is already available, no need to add it!
|
|
4378
|
+
if (!this.invisible.has(changeTree)) {
|
|
4378
4379
|
return;
|
|
4379
4380
|
}
|
|
4380
|
-
this.addParent(parentChangeTree, tag);
|
|
4381
4381
|
// add parent's tag properties
|
|
4382
|
-
if (
|
|
4383
|
-
let
|
|
4384
|
-
if (
|
|
4385
|
-
|
|
4386
|
-
this.changes.set(
|
|
4382
|
+
if (changeTree.getChange(parentIndex) !== OPERATION.DELETE) {
|
|
4383
|
+
let changes = this.changes.get(changeTree);
|
|
4384
|
+
if (changes === undefined) {
|
|
4385
|
+
changes = new Map();
|
|
4386
|
+
this.changes.set(changeTree, changes);
|
|
4387
4387
|
}
|
|
4388
|
-
// console.log("add parent change", {
|
|
4389
|
-
// parentIndex,
|
|
4390
|
-
// parentChanges,
|
|
4391
|
-
// parentChange: (
|
|
4392
|
-
// parentChangeTree.getChange(parentIndex) &&
|
|
4393
|
-
// OPERATION[parentChangeTree.getChange(parentIndex)]
|
|
4394
|
-
// ),
|
|
4395
|
-
// })
|
|
4396
4388
|
if (!this.tags) {
|
|
4397
4389
|
this.tags = new WeakMap();
|
|
4398
4390
|
}
|
|
4399
4391
|
let tags;
|
|
4400
|
-
if (!this.tags.has(
|
|
4392
|
+
if (!this.tags.has(changeTree)) {
|
|
4401
4393
|
tags = new Set();
|
|
4402
|
-
this.tags.set(
|
|
4394
|
+
this.tags.set(changeTree, tags);
|
|
4403
4395
|
}
|
|
4404
4396
|
else {
|
|
4405
|
-
tags = this.tags.get(
|
|
4397
|
+
tags = this.tags.get(changeTree);
|
|
4406
4398
|
}
|
|
4407
4399
|
tags.add(tag);
|
|
4408
|
-
|
|
4400
|
+
changes.set(parentIndex, OPERATION.ADD);
|
|
4409
4401
|
}
|
|
4410
4402
|
}
|
|
4411
4403
|
remove(obj, tag = DEFAULT_VIEW_TAG) {
|