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