@colyseus/schema 3.0.19 → 3.0.21
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 +103 -62
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +103 -62
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +103 -62
- package/lib/Reflection.js +69 -41
- package/lib/Reflection.js.map +1 -1
- package/lib/encoder/Encoder.js +6 -4
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/StateView.js +7 -8
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/types/TypeContext.d.ts +2 -0
- package/lib/types/TypeContext.js +9 -5
- package/lib/types/TypeContext.js.map +1 -1
- package/lib/types/custom/CollectionSchema.d.ts +5 -1
- package/lib/types/custom/CollectionSchema.js +6 -2
- package/lib/types/custom/CollectionSchema.js.map +1 -1
- package/lib/types/custom/MapSchema.js +0 -1
- package/lib/types/custom/MapSchema.js.map +1 -1
- package/lib/types/custom/SetSchema.d.ts +5 -1
- package/lib/types/custom/SetSchema.js +6 -2
- package/lib/types/custom/SetSchema.js.map +1 -1
- package/package.json +1 -1
- package/src/Reflection.ts +79 -46
- package/src/encoder/Encoder.ts +6 -4
- package/src/encoder/StateView.ts +3 -2
- package/src/types/TypeContext.ts +10 -5
- package/src/types/custom/CollectionSchema.ts +9 -3
- package/src/types/custom/MapSchema.ts +1 -1
- package/src/types/custom/SetSchema.ts +8 -3
package/build/cjs/index.js
CHANGED
|
@@ -600,6 +600,7 @@ class TypeContext {
|
|
|
600
600
|
* Keeps track of which classes extends which. (parent -> children)
|
|
601
601
|
*/
|
|
602
602
|
static { this.inheritedTypes = new Map(); }
|
|
603
|
+
static { this.cachedContexts = new Map(); }
|
|
603
604
|
static register(target) {
|
|
604
605
|
const parent = Object.getPrototypeOf(target);
|
|
605
606
|
if (parent !== Schema) {
|
|
@@ -611,17 +612,20 @@ class TypeContext {
|
|
|
611
612
|
inherits.add(target);
|
|
612
613
|
}
|
|
613
614
|
}
|
|
615
|
+
static cache(rootClass) {
|
|
616
|
+
let context = TypeContext.cachedContexts.get(rootClass);
|
|
617
|
+
if (!context) {
|
|
618
|
+
context = new TypeContext(rootClass);
|
|
619
|
+
TypeContext.cachedContexts.set(rootClass, context);
|
|
620
|
+
}
|
|
621
|
+
return context;
|
|
622
|
+
}
|
|
614
623
|
constructor(rootClass) {
|
|
615
624
|
this.types = {};
|
|
616
625
|
this.schemas = new Map();
|
|
617
626
|
this.hasFilters = false;
|
|
618
627
|
this.parentFiltered = {};
|
|
619
628
|
if (rootClass) {
|
|
620
|
-
//
|
|
621
|
-
// TODO:
|
|
622
|
-
// cache "discoverTypes" results for each rootClass
|
|
623
|
-
// to avoid re-discovering types for each new context/room
|
|
624
|
-
//
|
|
625
629
|
this.discoverTypes(rootClass);
|
|
626
630
|
}
|
|
627
631
|
}
|
|
@@ -3313,7 +3317,7 @@ class CollectionSchema {
|
|
|
3313
3317
|
static [(_a$1 = $encoder, _b$1 = $decoder, $filter)](ref, index, view) {
|
|
3314
3318
|
return (!view ||
|
|
3315
3319
|
typeof (ref[$childType]) === "string" ||
|
|
3316
|
-
view.items.has(ref[$getByIndex](index)[$changes]));
|
|
3320
|
+
view.items.has((ref[$getByIndex](index) ?? ref.deletedItems[index])[$changes]));
|
|
3317
3321
|
}
|
|
3318
3322
|
static is(type) {
|
|
3319
3323
|
return type['collection'] !== undefined;
|
|
@@ -3321,6 +3325,7 @@ class CollectionSchema {
|
|
|
3321
3325
|
constructor(initialValues) {
|
|
3322
3326
|
this.$items = new Map();
|
|
3323
3327
|
this.$indexes = new Map();
|
|
3328
|
+
this.deletedItems = {};
|
|
3324
3329
|
this.$refId = 0;
|
|
3325
3330
|
this[$changes] = new ChangeTree(this);
|
|
3326
3331
|
this[$changes].indexes = {};
|
|
@@ -3370,7 +3375,7 @@ class CollectionSchema {
|
|
|
3370
3375
|
if (index === undefined) {
|
|
3371
3376
|
return false;
|
|
3372
3377
|
}
|
|
3373
|
-
this[$changes].delete(index);
|
|
3378
|
+
this.deletedItems[index] = this[$changes].delete(index);
|
|
3374
3379
|
this.$indexes.delete(index);
|
|
3375
3380
|
return this.$items.delete(index);
|
|
3376
3381
|
}
|
|
@@ -3415,6 +3420,9 @@ class CollectionSchema {
|
|
|
3415
3420
|
this.$items.delete(key);
|
|
3416
3421
|
this.$indexes.delete(index);
|
|
3417
3422
|
}
|
|
3423
|
+
[$onEncodeEnd]() {
|
|
3424
|
+
this.deletedItems = {};
|
|
3425
|
+
}
|
|
3418
3426
|
toArray() {
|
|
3419
3427
|
return Array.from(this.$items.values());
|
|
3420
3428
|
}
|
|
@@ -3469,7 +3477,7 @@ class SetSchema {
|
|
|
3469
3477
|
static [(_a = $encoder, _b = $decoder, $filter)](ref, index, view) {
|
|
3470
3478
|
return (!view ||
|
|
3471
3479
|
typeof (ref[$childType]) === "string" ||
|
|
3472
|
-
view.items.has(ref[$getByIndex](index)[$changes]));
|
|
3480
|
+
view.items.has((ref[$getByIndex](index) ?? ref.deletedItems[index])[$changes]));
|
|
3473
3481
|
}
|
|
3474
3482
|
static is(type) {
|
|
3475
3483
|
return type['set'] !== undefined;
|
|
@@ -3477,6 +3485,7 @@ class SetSchema {
|
|
|
3477
3485
|
constructor(initialValues) {
|
|
3478
3486
|
this.$items = new Map();
|
|
3479
3487
|
this.$indexes = new Map();
|
|
3488
|
+
this.deletedItems = {};
|
|
3480
3489
|
this.$refId = 0;
|
|
3481
3490
|
this[$changes] = new ChangeTree(this);
|
|
3482
3491
|
this[$changes].indexes = {};
|
|
@@ -3526,7 +3535,7 @@ class SetSchema {
|
|
|
3526
3535
|
if (index === undefined) {
|
|
3527
3536
|
return false;
|
|
3528
3537
|
}
|
|
3529
|
-
this[$changes].delete(index);
|
|
3538
|
+
this.deletedItems[index] = this[$changes].delete(index);
|
|
3530
3539
|
this.$indexes.delete(index);
|
|
3531
3540
|
return this.$items.delete(index);
|
|
3532
3541
|
}
|
|
@@ -3583,6 +3592,9 @@ class SetSchema {
|
|
|
3583
3592
|
this.$items.delete(key);
|
|
3584
3593
|
this.$indexes.delete(index);
|
|
3585
3594
|
}
|
|
3595
|
+
[$onEncodeEnd]() {
|
|
3596
|
+
this.deletedItems = {};
|
|
3597
|
+
}
|
|
3586
3598
|
toArray() {
|
|
3587
3599
|
return Array.from(this.$items.values());
|
|
3588
3600
|
}
|
|
@@ -3759,10 +3771,12 @@ class Encoder {
|
|
|
3759
3771
|
constructor(state) {
|
|
3760
3772
|
this.sharedBuffer = Buffer.allocUnsafe(Encoder.BUFFER_SIZE);
|
|
3761
3773
|
//
|
|
3762
|
-
//
|
|
3763
|
-
//
|
|
3774
|
+
// Use .cache() here to avoid re-creating a new context for every new room instance.
|
|
3775
|
+
//
|
|
3776
|
+
// We may need to make this optional in case of dynamically created
|
|
3777
|
+
// schemas - which would lead to memory leaks
|
|
3764
3778
|
//
|
|
3765
|
-
this.context =
|
|
3779
|
+
this.context = TypeContext.cache(state.constructor);
|
|
3766
3780
|
this.root = new Root(this.context);
|
|
3767
3781
|
this.setState(state);
|
|
3768
3782
|
// console.log(">>>>>>>>>>>>>>>> Encoder types");
|
|
@@ -3787,7 +3801,7 @@ class Encoder {
|
|
|
3787
3801
|
view.invisible.add(changeTree);
|
|
3788
3802
|
continue; // skip this change tree
|
|
3789
3803
|
}
|
|
3790
|
-
else
|
|
3804
|
+
else {
|
|
3791
3805
|
view.invisible.delete(changeTree); // remove from invisible list
|
|
3792
3806
|
}
|
|
3793
3807
|
}
|
|
@@ -4267,50 +4281,28 @@ class Reflection extends Schema {
|
|
|
4267
4281
|
if (rootType > 0) {
|
|
4268
4282
|
reflection.rootType = rootType;
|
|
4269
4283
|
}
|
|
4270
|
-
const
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
const
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
if (typeof (type) === "string") {
|
|
4283
|
-
fieldType = type;
|
|
4284
|
+
const includedTypeIds = new Set();
|
|
4285
|
+
const pendingReflectionTypes = {};
|
|
4286
|
+
// add type to reflection in a way that respects inheritance
|
|
4287
|
+
// (parent types should be added before their children)
|
|
4288
|
+
const addType = (type) => {
|
|
4289
|
+
if (type.extendsId === undefined || includedTypeIds.has(type.extendsId)) {
|
|
4290
|
+
includedTypeIds.add(type.id);
|
|
4291
|
+
reflection.types.push(type);
|
|
4292
|
+
const deps = pendingReflectionTypes[type.id];
|
|
4293
|
+
if (deps !== undefined) {
|
|
4294
|
+
delete pendingReflectionTypes[type.id];
|
|
4295
|
+
deps.forEach((childType) => addType(childType));
|
|
4284
4296
|
}
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
//
|
|
4290
|
-
if (Schema.is(type)) {
|
|
4291
|
-
fieldType = "ref";
|
|
4292
|
-
childTypeSchema = type;
|
|
4293
|
-
}
|
|
4294
|
-
else {
|
|
4295
|
-
fieldType = Object.keys(type)[0];
|
|
4296
|
-
if (typeof (type[fieldType]) === "string") {
|
|
4297
|
-
fieldType += ":" + type[fieldType]; // array:string
|
|
4298
|
-
}
|
|
4299
|
-
else {
|
|
4300
|
-
childTypeSchema = type[fieldType];
|
|
4301
|
-
}
|
|
4302
|
-
}
|
|
4303
|
-
field.referencedType = (childTypeSchema)
|
|
4304
|
-
? context.getTypeId(childTypeSchema)
|
|
4305
|
-
: -1;
|
|
4297
|
+
}
|
|
4298
|
+
else {
|
|
4299
|
+
if (pendingReflectionTypes[type.extendsId] === undefined) {
|
|
4300
|
+
pendingReflectionTypes[type.extendsId] = [];
|
|
4306
4301
|
}
|
|
4307
|
-
|
|
4308
|
-
currentType.fields.push(field);
|
|
4302
|
+
pendingReflectionTypes[type.extendsId].push(type);
|
|
4309
4303
|
}
|
|
4310
|
-
reflection.types.push(currentType);
|
|
4311
4304
|
};
|
|
4312
|
-
|
|
4313
|
-
const klass = context.types[typeid];
|
|
4305
|
+
context.schemas.forEach((typeid, klass) => {
|
|
4314
4306
|
const type = new ReflectionType();
|
|
4315
4307
|
type.id = Number(typeid);
|
|
4316
4308
|
// support inheritance
|
|
@@ -4318,7 +4310,57 @@ class Reflection extends Schema {
|
|
|
4318
4310
|
if (inheritFrom !== Schema) {
|
|
4319
4311
|
type.extendsId = context.schemas.get(inheritFrom);
|
|
4320
4312
|
}
|
|
4321
|
-
|
|
4313
|
+
const metadata = klass[Symbol.metadata];
|
|
4314
|
+
//
|
|
4315
|
+
// FIXME: this is a workaround for inherited types without additional fields
|
|
4316
|
+
// if metadata is the same reference as the parent class - it means the class has no own metadata
|
|
4317
|
+
//
|
|
4318
|
+
if (metadata !== inheritFrom[Symbol.metadata]) {
|
|
4319
|
+
for (const fieldIndex in metadata) {
|
|
4320
|
+
const index = Number(fieldIndex);
|
|
4321
|
+
const fieldName = metadata[index].name;
|
|
4322
|
+
// skip fields from parent classes
|
|
4323
|
+
if (!Object.prototype.hasOwnProperty.call(metadata, fieldName)) {
|
|
4324
|
+
continue;
|
|
4325
|
+
}
|
|
4326
|
+
const reflectionField = new ReflectionField();
|
|
4327
|
+
reflectionField.name = fieldName;
|
|
4328
|
+
let fieldType;
|
|
4329
|
+
const field = metadata[index];
|
|
4330
|
+
if (typeof (field.type) === "string") {
|
|
4331
|
+
fieldType = field.type;
|
|
4332
|
+
}
|
|
4333
|
+
else {
|
|
4334
|
+
let childTypeSchema;
|
|
4335
|
+
//
|
|
4336
|
+
// TODO: refactor below.
|
|
4337
|
+
//
|
|
4338
|
+
if (Schema.is(field.type)) {
|
|
4339
|
+
fieldType = "ref";
|
|
4340
|
+
childTypeSchema = field.type;
|
|
4341
|
+
}
|
|
4342
|
+
else {
|
|
4343
|
+
fieldType = Object.keys(field.type)[0];
|
|
4344
|
+
if (typeof (field.type[fieldType]) === "string") {
|
|
4345
|
+
fieldType += ":" + field.type[fieldType]; // array:string
|
|
4346
|
+
}
|
|
4347
|
+
else {
|
|
4348
|
+
childTypeSchema = field.type[fieldType];
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
4351
|
+
reflectionField.referencedType = (childTypeSchema)
|
|
4352
|
+
? context.getTypeId(childTypeSchema)
|
|
4353
|
+
: -1;
|
|
4354
|
+
}
|
|
4355
|
+
reflectionField.type = fieldType;
|
|
4356
|
+
type.fields.push(reflectionField);
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
addType(type);
|
|
4360
|
+
});
|
|
4361
|
+
// in case there are types that were not added due to inheritance
|
|
4362
|
+
for (const typeid in pendingReflectionTypes) {
|
|
4363
|
+
pendingReflectionTypes[typeid].forEach((type) => reflection.types.push(type));
|
|
4322
4364
|
}
|
|
4323
4365
|
const buf = reflectionEncoder.encodeAll(it);
|
|
4324
4366
|
return Buffer.from(buf, 0, it.offset);
|
|
@@ -4743,10 +4785,11 @@ class StateView {
|
|
|
4743
4785
|
} // skip "undefined" indexes
|
|
4744
4786
|
const op = changeTree.indexedOperations[index] ?? exports.OPERATION.ADD;
|
|
4745
4787
|
const tagAtIndex = metadata?.[index].tag;
|
|
4746
|
-
if (
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4788
|
+
if (!changeTree.isNew && // new structures will be added as part of .encode() call, no need to force it to .encodeView()
|
|
4789
|
+
(isInvisible || // if "invisible", include all
|
|
4790
|
+
tagAtIndex === undefined || // "all change" with no tag
|
|
4791
|
+
tagAtIndex === tag // tagged property
|
|
4792
|
+
) &&
|
|
4750
4793
|
op !== exports.OPERATION.DELETE) {
|
|
4751
4794
|
changes[index] = op;
|
|
4752
4795
|
}
|
|
@@ -4775,10 +4818,8 @@ class StateView {
|
|
|
4775
4818
|
if (parentChangeTree && (parentChangeTree.filteredChanges !== undefined)) {
|
|
4776
4819
|
this.addParentOf(changeTree, tag);
|
|
4777
4820
|
}
|
|
4778
|
-
// parent is already available, no need to add it!
|
|
4779
|
-
if (!this.invisible.has(changeTree)) {
|
|
4780
|
-
return;
|
|
4781
|
-
}
|
|
4821
|
+
// // parent is already available, no need to add it!
|
|
4822
|
+
// if (!this.invisible.has(changeTree)) { return; }
|
|
4782
4823
|
}
|
|
4783
4824
|
// add parent's tag properties
|
|
4784
4825
|
if (changeTree.getChange(parentIndex) !== exports.OPERATION.DELETE) {
|