@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/esm/index.mjs
CHANGED
|
@@ -146,7 +146,7 @@ const Metadata = {
|
|
|
146
146
|
// TODO: remove/refactor this...
|
|
147
147
|
//
|
|
148
148
|
const metadata = {};
|
|
149
|
-
klass
|
|
149
|
+
klass[Symbol.metadata] = metadata;
|
|
150
150
|
Object.defineProperty(metadata, -1, {
|
|
151
151
|
value: 0,
|
|
152
152
|
enumerable: false,
|
|
@@ -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);
|
|
@@ -2413,19 +2415,25 @@ class TypeContext {
|
|
|
2413
2415
|
return false;
|
|
2414
2416
|
}
|
|
2415
2417
|
this.types[typeid] = schema;
|
|
2418
|
+
//
|
|
2419
|
+
// Workaround to allow using an empty Schema (with no `@type()` fields)
|
|
2420
|
+
//
|
|
2421
|
+
if (schema[Symbol.metadata] === undefined) {
|
|
2422
|
+
Metadata.init(schema);
|
|
2423
|
+
}
|
|
2416
2424
|
this.schemas.set(schema, typeid);
|
|
2417
2425
|
return true;
|
|
2418
2426
|
}
|
|
2419
2427
|
getTypeId(klass) {
|
|
2420
2428
|
return this.schemas.get(klass);
|
|
2421
2429
|
}
|
|
2422
|
-
discoverTypes(klass) {
|
|
2430
|
+
discoverTypes(klass, parentFieldViewTag) {
|
|
2423
2431
|
if (!this.add(klass)) {
|
|
2424
2432
|
return;
|
|
2425
2433
|
}
|
|
2426
2434
|
// add classes inherited from this base class
|
|
2427
2435
|
TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
|
|
2428
|
-
this.discoverTypes(child);
|
|
2436
|
+
this.discoverTypes(child, parentFieldViewTag);
|
|
2429
2437
|
});
|
|
2430
2438
|
// skip if no fields are defined for this class.
|
|
2431
2439
|
if (klass[Symbol.metadata] === undefined) {
|
|
@@ -2438,7 +2446,15 @@ class TypeContext {
|
|
|
2438
2446
|
this.hasFilters = true;
|
|
2439
2447
|
}
|
|
2440
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
|
+
}
|
|
2441
2456
|
const fieldType = metadata[field].type;
|
|
2457
|
+
const viewTag = metadata[field].tag;
|
|
2442
2458
|
if (typeof (fieldType) === "string") {
|
|
2443
2459
|
continue;
|
|
2444
2460
|
}
|
|
@@ -2447,10 +2463,10 @@ class TypeContext {
|
|
|
2447
2463
|
if (type === "string") {
|
|
2448
2464
|
continue;
|
|
2449
2465
|
}
|
|
2450
|
-
this.discoverTypes(type);
|
|
2466
|
+
this.discoverTypes(type, viewTag);
|
|
2451
2467
|
}
|
|
2452
2468
|
else if (typeof (fieldType) === "function") {
|
|
2453
|
-
this.discoverTypes(fieldType);
|
|
2469
|
+
this.discoverTypes(fieldType, viewTag);
|
|
2454
2470
|
}
|
|
2455
2471
|
else {
|
|
2456
2472
|
const type = Object.values(fieldType)[0];
|
|
@@ -2458,7 +2474,7 @@ class TypeContext {
|
|
|
2458
2474
|
if (typeof (type) === "string") {
|
|
2459
2475
|
continue;
|
|
2460
2476
|
}
|
|
2461
|
-
this.discoverTypes(type);
|
|
2477
|
+
this.discoverTypes(type, viewTag);
|
|
2462
2478
|
}
|
|
2463
2479
|
}
|
|
2464
2480
|
}
|
|
@@ -3017,13 +3033,13 @@ class Schema {
|
|
|
3017
3033
|
}
|
|
3018
3034
|
return output;
|
|
3019
3035
|
}
|
|
3020
|
-
static debugChangesDeep(ref) {
|
|
3036
|
+
static debugChangesDeep(ref, changeSetName = "changes") {
|
|
3021
3037
|
let output = "";
|
|
3022
3038
|
const rootChangeTree = ref[$changes];
|
|
3023
3039
|
const changeTrees = new Map();
|
|
3024
3040
|
let totalInstances = 0;
|
|
3025
3041
|
let totalOperations = 0;
|
|
3026
|
-
for (const [changeTree, changes] of (rootChangeTree.root.
|
|
3042
|
+
for (const [changeTree, changes] of (rootChangeTree.root[changeSetName].entries())) {
|
|
3027
3043
|
let includeChangeTree = false;
|
|
3028
3044
|
let parentChangeTrees = [];
|
|
3029
3045
|
let parentChangeTree = changeTree.parent?.[$changes];
|
|
@@ -3426,30 +3442,26 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
3426
3442
|
|
|
3427
3443
|
class Encoder {
|
|
3428
3444
|
static { this.BUFFER_SIZE = 8 * 1024; } // 8KB
|
|
3429
|
-
constructor(
|
|
3445
|
+
constructor(state) {
|
|
3430
3446
|
this.sharedBuffer = Buffer.allocUnsafeSlow(Encoder.BUFFER_SIZE);
|
|
3431
|
-
this.
|
|
3447
|
+
this.root = new Root();
|
|
3432
3448
|
//
|
|
3433
3449
|
// TODO: cache and restore "Context" based on root schema
|
|
3434
3450
|
// (to avoid creating a new context for every new room)
|
|
3435
3451
|
//
|
|
3436
|
-
this.context = new TypeContext(
|
|
3452
|
+
this.context = new TypeContext(state.constructor);
|
|
3453
|
+
this.setState(state);
|
|
3437
3454
|
// console.log(">>>>>>>>>>>>>>>> Encoder types");
|
|
3438
3455
|
// this.context.schemas.forEach((id, schema) => {
|
|
3439
3456
|
// console.log("type:", id, schema.name, Object.keys(schema[Symbol.metadata]));
|
|
3440
3457
|
// });
|
|
3441
3458
|
}
|
|
3442
|
-
|
|
3443
|
-
this.root = new Root();
|
|
3459
|
+
setState(state) {
|
|
3444
3460
|
this.state = state;
|
|
3445
|
-
|
|
3446
|
-
if (state.constructor[Symbol.metadata] === undefined) {
|
|
3447
|
-
Metadata.init(state);
|
|
3448
|
-
}
|
|
3449
|
-
state[$changes].setRoot(this.root);
|
|
3461
|
+
this.state[$changes].setRoot(this.root);
|
|
3450
3462
|
}
|
|
3451
|
-
encode(it = { offset: 0 }, view, buffer = this.sharedBuffer, changeTrees = this.root.changes, isEncodeAll = this.root.allChanges === changeTrees
|
|
3452
|
-
|
|
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
|
+
) {
|
|
3453
3465
|
const hasView = (view !== undefined);
|
|
3454
3466
|
const rootChangeTree = this.state[$changes];
|
|
3455
3467
|
const changeTreesIterator = changeTrees.entries();
|
|
@@ -3488,7 +3500,6 @@ class Encoder {
|
|
|
3488
3500
|
// TODO: avoid checking if no view tags were defined
|
|
3489
3501
|
//
|
|
3490
3502
|
if (filter && !filter(ref, fieldIndex, view)) {
|
|
3491
|
-
// console.log("SKIP FIELD:", { ref: changeTree.ref.constructor.name, fieldIndex, })
|
|
3492
3503
|
// console.log("ADD AS INVISIBLE:", fieldIndex, changeTree.ref.constructor.name)
|
|
3493
3504
|
// view?.invisible.add(changeTree);
|
|
3494
3505
|
continue;
|
|
@@ -3566,8 +3577,6 @@ class Encoder {
|
|
|
3566
3577
|
}
|
|
3567
3578
|
encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3568
3579
|
const viewOffset = it.offset;
|
|
3569
|
-
// try to encode "filtered" changes
|
|
3570
|
-
this.encode(it, view, bytes, this.root.filteredChanges);
|
|
3571
3580
|
// encode visibility changes (add/remove for this view)
|
|
3572
3581
|
const viewChangesIterator = view.changes.entries();
|
|
3573
3582
|
for (const [changeTree, changes] of viewChangesIterator) {
|
|
@@ -3594,6 +3603,8 @@ class Encoder {
|
|
|
3594
3603
|
//
|
|
3595
3604
|
// clear "view" changes after encoding
|
|
3596
3605
|
view.changes.clear();
|
|
3606
|
+
// try to encode "filtered" changes
|
|
3607
|
+
this.encode(it, view, bytes, this.root.filteredChanges, false, viewOffset);
|
|
3597
3608
|
return Buffer.concat([
|
|
3598
3609
|
bytes.subarray(0, sharedOffset),
|
|
3599
3610
|
bytes.subarray(viewOffset, it.offset)
|
|
@@ -3766,14 +3777,14 @@ class ReferenceTracker {
|
|
|
3766
3777
|
class Decoder {
|
|
3767
3778
|
constructor(root, context) {
|
|
3768
3779
|
this.currentRefId = 0;
|
|
3769
|
-
this.
|
|
3780
|
+
this.setState(root);
|
|
3770
3781
|
this.context = context || new TypeContext(root.constructor);
|
|
3771
3782
|
// console.log(">>>>>>>>>>>>>>>> Decoder types");
|
|
3772
3783
|
// this.context.schemas.forEach((id, schema) => {
|
|
3773
3784
|
// console.log("type:", id, schema.name, Object.keys(schema[Symbol.metadata]));
|
|
3774
3785
|
// });
|
|
3775
3786
|
}
|
|
3776
|
-
|
|
3787
|
+
setState(root) {
|
|
3777
3788
|
this.state = root;
|
|
3778
3789
|
this.root = new ReferenceTracker();
|
|
3779
3790
|
this.root.addRef(0, root);
|
|
@@ -3902,9 +3913,7 @@ class Reflection extends Schema {
|
|
|
3902
3913
|
this.types = new ArraySchema();
|
|
3903
3914
|
}
|
|
3904
3915
|
static encode(instance, context, it = { offset: 0 }) {
|
|
3905
|
-
|
|
3906
|
-
context = new TypeContext(instance.constructor);
|
|
3907
|
-
}
|
|
3916
|
+
context ??= new TypeContext(instance.constructor);
|
|
3908
3917
|
const reflection = new Reflection();
|
|
3909
3918
|
const encoder = new Encoder(reflection);
|
|
3910
3919
|
const buildType = (currentType, metadata) => {
|
|
@@ -4286,26 +4295,21 @@ class StateView {
|
|
|
4286
4295
|
this.changes = new Map();
|
|
4287
4296
|
}
|
|
4288
4297
|
// TODO: allow to set multiple tags at once
|
|
4289
|
-
add(obj, tag = DEFAULT_VIEW_TAG) {
|
|
4298
|
+
add(obj, tag = DEFAULT_VIEW_TAG, checkIncludeParent = true) {
|
|
4290
4299
|
if (!obj[$changes]) {
|
|
4291
4300
|
console.warn("StateView#add(), invalid object:", obj);
|
|
4292
4301
|
return this;
|
|
4293
4302
|
}
|
|
4294
4303
|
// FIXME: ArraySchema/MapSchema does not have metadata
|
|
4295
4304
|
const metadata = obj.constructor[Symbol.metadata];
|
|
4296
|
-
|
|
4305
|
+
const changeTree = obj[$changes];
|
|
4297
4306
|
this.items.add(changeTree);
|
|
4298
|
-
//
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
this.add(change.ref, tag);
|
|
4305
|
-
});
|
|
4306
|
-
// add parent ChangeTree's, if they are invisible to this view
|
|
4307
|
-
// TODO: REFACTOR addParent()
|
|
4308
|
-
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
|
+
}
|
|
4309
4313
|
//
|
|
4310
4314
|
// TODO: when adding an item of a MapSchema, the changes may not
|
|
4311
4315
|
// be set (only the parent's changes are set)
|
|
@@ -4337,73 +4341,63 @@ class StateView {
|
|
|
4337
4341
|
});
|
|
4338
4342
|
}
|
|
4339
4343
|
else {
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
// metadata?.[-3]?.[DEFAULT_VIEW_TAG]?.forEach((index) => {
|
|
4343
|
-
// if (changeTree.getChange(index) !== OPERATION.DELETE) {
|
|
4344
|
-
// changes.set(index, OPERATION.ADD);
|
|
4345
|
-
// }
|
|
4346
|
-
// });
|
|
4347
|
-
const allChangesSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
|
|
4344
|
+
const isInvisible = this.invisible.has(changeTree);
|
|
4345
|
+
const changeSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
|
|
4348
4346
|
? changeTree.allFilteredChanges
|
|
4349
4347
|
: changeTree.allChanges;
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
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);
|
|
4356
4356
|
}
|
|
4357
|
-
}
|
|
4358
|
-
}
|
|
4359
|
-
// TODO: avoid unnecessary iteration here
|
|
4360
|
-
while (changeTree.parent &&
|
|
4361
|
-
(changeTree = changeTree.parent[$changes]) &&
|
|
4362
|
-
(changeTree.isFiltered || changeTree.isPartiallyFiltered)) {
|
|
4363
|
-
this.items.add(changeTree);
|
|
4357
|
+
});
|
|
4364
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
|
+
});
|
|
4365
4367
|
return this;
|
|
4366
4368
|
}
|
|
4367
|
-
addParent(changeTree, tag) {
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
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);
|
|
4371
4376
|
}
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
if (!this.invisible.has(parentChangeTree)) {
|
|
4375
|
-
// 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)) {
|
|
4376
4379
|
return;
|
|
4377
4380
|
}
|
|
4378
|
-
this.addParent(parentChangeTree, tag);
|
|
4379
4381
|
// add parent's tag properties
|
|
4380
|
-
if (
|
|
4381
|
-
let
|
|
4382
|
-
if (
|
|
4383
|
-
|
|
4384
|
-
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);
|
|
4385
4387
|
}
|
|
4386
|
-
// console.log("add parent change", {
|
|
4387
|
-
// parentIndex,
|
|
4388
|
-
// parentChanges,
|
|
4389
|
-
// parentChange: (
|
|
4390
|
-
// parentChangeTree.getChange(parentIndex) &&
|
|
4391
|
-
// OPERATION[parentChangeTree.getChange(parentIndex)]
|
|
4392
|
-
// ),
|
|
4393
|
-
// })
|
|
4394
4388
|
if (!this.tags) {
|
|
4395
4389
|
this.tags = new WeakMap();
|
|
4396
4390
|
}
|
|
4397
4391
|
let tags;
|
|
4398
|
-
if (!this.tags.has(
|
|
4392
|
+
if (!this.tags.has(changeTree)) {
|
|
4399
4393
|
tags = new Set();
|
|
4400
|
-
this.tags.set(
|
|
4394
|
+
this.tags.set(changeTree, tags);
|
|
4401
4395
|
}
|
|
4402
4396
|
else {
|
|
4403
|
-
tags = this.tags.get(
|
|
4397
|
+
tags = this.tags.get(changeTree);
|
|
4404
4398
|
}
|
|
4405
4399
|
tags.add(tag);
|
|
4406
|
-
|
|
4400
|
+
changes.set(parentIndex, OPERATION.ADD);
|
|
4407
4401
|
}
|
|
4408
4402
|
}
|
|
4409
4403
|
remove(obj, tag = DEFAULT_VIEW_TAG) {
|