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