@colyseus/schema 3.0.20 → 3.0.22
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 +91 -56
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +91 -56
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +91 -56
- package/lib/Reflection.js +69 -41
- package/lib/Reflection.js.map +1 -1
- package/lib/decoder/ReferenceTracker.js +2 -3
- package/lib/decoder/ReferenceTracker.js.map +1 -1
- package/lib/encoder/Encoder.js +7 -4
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/StateView.js +4 -4
- 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/package.json +1 -1
- package/src/Reflection.ts +79 -46
- package/src/decoder/ReferenceTracker.ts +2 -2
- package/src/encoder/Encoder.ts +7 -4
- package/src/encoder/StateView.ts +5 -5
- package/src/types/TypeContext.ts +10 -5
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
|
}
|
|
@@ -3767,10 +3771,12 @@ class Encoder {
|
|
|
3767
3771
|
constructor(state) {
|
|
3768
3772
|
this.sharedBuffer = Buffer.allocUnsafe(Encoder.BUFFER_SIZE);
|
|
3769
3773
|
//
|
|
3770
|
-
//
|
|
3771
|
-
//
|
|
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
|
|
3772
3778
|
//
|
|
3773
|
-
this.context =
|
|
3779
|
+
this.context = TypeContext.cache(state.constructor);
|
|
3774
3780
|
this.root = new Root(this.context);
|
|
3775
3781
|
this.setState(state);
|
|
3776
3782
|
// console.log(">>>>>>>>>>>>>>>> Encoder types");
|
|
@@ -3917,13 +3923,14 @@ class Encoder {
|
|
|
3917
3923
|
const changeTree = this.root.changeTrees[refId];
|
|
3918
3924
|
if (changeTree === undefined) {
|
|
3919
3925
|
// detached instance, remove from view and skip.
|
|
3926
|
+
// console.log("detached instance, remove from view and skip.", refId);
|
|
3920
3927
|
view.changes.delete(refId);
|
|
3921
3928
|
continue;
|
|
3922
3929
|
}
|
|
3923
3930
|
const keys = Object.keys(changes);
|
|
3924
3931
|
if (keys.length === 0) {
|
|
3925
3932
|
// FIXME: avoid having empty changes if no changes were made
|
|
3926
|
-
// console.log("changes.size === 0, skip", changeTree.ref.constructor.name);
|
|
3933
|
+
// console.log("changes.size === 0, skip", refId, changeTree.ref.constructor.name);
|
|
3927
3934
|
continue;
|
|
3928
3935
|
}
|
|
3929
3936
|
const ref = changeTree.ref;
|
|
@@ -4069,7 +4076,7 @@ class ReferenceTracker {
|
|
|
4069
4076
|
//
|
|
4070
4077
|
// Ensure child schema instances have their references removed as well.
|
|
4071
4078
|
//
|
|
4072
|
-
if (
|
|
4079
|
+
if (ref.constructor[Symbol.metadata] !== undefined) {
|
|
4073
4080
|
const metadata = ref.constructor[Symbol.metadata];
|
|
4074
4081
|
for (const index in metadata) {
|
|
4075
4082
|
const field = metadata[index].name;
|
|
@@ -4080,7 +4087,7 @@ class ReferenceTracker {
|
|
|
4080
4087
|
}
|
|
4081
4088
|
}
|
|
4082
4089
|
else {
|
|
4083
|
-
if (typeof (
|
|
4090
|
+
if (typeof (ref[$childType]) === "function") {
|
|
4084
4091
|
Array.from(ref.values())
|
|
4085
4092
|
.forEach((child) => {
|
|
4086
4093
|
const childRefId = this.refIds.get(child);
|
|
@@ -4275,50 +4282,28 @@ class Reflection extends Schema {
|
|
|
4275
4282
|
if (rootType > 0) {
|
|
4276
4283
|
reflection.rootType = rootType;
|
|
4277
4284
|
}
|
|
4278
|
-
const
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
const
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
if (typeof (type) === "string") {
|
|
4291
|
-
fieldType = type;
|
|
4285
|
+
const includedTypeIds = new Set();
|
|
4286
|
+
const pendingReflectionTypes = {};
|
|
4287
|
+
// add type to reflection in a way that respects inheritance
|
|
4288
|
+
// (parent types should be added before their children)
|
|
4289
|
+
const addType = (type) => {
|
|
4290
|
+
if (type.extendsId === undefined || includedTypeIds.has(type.extendsId)) {
|
|
4291
|
+
includedTypeIds.add(type.id);
|
|
4292
|
+
reflection.types.push(type);
|
|
4293
|
+
const deps = pendingReflectionTypes[type.id];
|
|
4294
|
+
if (deps !== undefined) {
|
|
4295
|
+
delete pendingReflectionTypes[type.id];
|
|
4296
|
+
deps.forEach((childType) => addType(childType));
|
|
4292
4297
|
}
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
//
|
|
4298
|
-
if (Schema.is(type)) {
|
|
4299
|
-
fieldType = "ref";
|
|
4300
|
-
childTypeSchema = type;
|
|
4301
|
-
}
|
|
4302
|
-
else {
|
|
4303
|
-
fieldType = Object.keys(type)[0];
|
|
4304
|
-
if (typeof (type[fieldType]) === "string") {
|
|
4305
|
-
fieldType += ":" + type[fieldType]; // array:string
|
|
4306
|
-
}
|
|
4307
|
-
else {
|
|
4308
|
-
childTypeSchema = type[fieldType];
|
|
4309
|
-
}
|
|
4310
|
-
}
|
|
4311
|
-
field.referencedType = (childTypeSchema)
|
|
4312
|
-
? context.getTypeId(childTypeSchema)
|
|
4313
|
-
: -1;
|
|
4298
|
+
}
|
|
4299
|
+
else {
|
|
4300
|
+
if (pendingReflectionTypes[type.extendsId] === undefined) {
|
|
4301
|
+
pendingReflectionTypes[type.extendsId] = [];
|
|
4314
4302
|
}
|
|
4315
|
-
|
|
4316
|
-
currentType.fields.push(field);
|
|
4303
|
+
pendingReflectionTypes[type.extendsId].push(type);
|
|
4317
4304
|
}
|
|
4318
|
-
reflection.types.push(currentType);
|
|
4319
4305
|
};
|
|
4320
|
-
|
|
4321
|
-
const klass = context.types[typeid];
|
|
4306
|
+
context.schemas.forEach((typeid, klass) => {
|
|
4322
4307
|
const type = new ReflectionType();
|
|
4323
4308
|
type.id = Number(typeid);
|
|
4324
4309
|
// support inheritance
|
|
@@ -4326,7 +4311,57 @@ class Reflection extends Schema {
|
|
|
4326
4311
|
if (inheritFrom !== Schema) {
|
|
4327
4312
|
type.extendsId = context.schemas.get(inheritFrom);
|
|
4328
4313
|
}
|
|
4329
|
-
|
|
4314
|
+
const metadata = klass[Symbol.metadata];
|
|
4315
|
+
//
|
|
4316
|
+
// FIXME: this is a workaround for inherited types without additional fields
|
|
4317
|
+
// if metadata is the same reference as the parent class - it means the class has no own metadata
|
|
4318
|
+
//
|
|
4319
|
+
if (metadata !== inheritFrom[Symbol.metadata]) {
|
|
4320
|
+
for (const fieldIndex in metadata) {
|
|
4321
|
+
const index = Number(fieldIndex);
|
|
4322
|
+
const fieldName = metadata[index].name;
|
|
4323
|
+
// skip fields from parent classes
|
|
4324
|
+
if (!Object.prototype.hasOwnProperty.call(metadata, fieldName)) {
|
|
4325
|
+
continue;
|
|
4326
|
+
}
|
|
4327
|
+
const reflectionField = new ReflectionField();
|
|
4328
|
+
reflectionField.name = fieldName;
|
|
4329
|
+
let fieldType;
|
|
4330
|
+
const field = metadata[index];
|
|
4331
|
+
if (typeof (field.type) === "string") {
|
|
4332
|
+
fieldType = field.type;
|
|
4333
|
+
}
|
|
4334
|
+
else {
|
|
4335
|
+
let childTypeSchema;
|
|
4336
|
+
//
|
|
4337
|
+
// TODO: refactor below.
|
|
4338
|
+
//
|
|
4339
|
+
if (Schema.is(field.type)) {
|
|
4340
|
+
fieldType = "ref";
|
|
4341
|
+
childTypeSchema = field.type;
|
|
4342
|
+
}
|
|
4343
|
+
else {
|
|
4344
|
+
fieldType = Object.keys(field.type)[0];
|
|
4345
|
+
if (typeof (field.type[fieldType]) === "string") {
|
|
4346
|
+
fieldType += ":" + field.type[fieldType]; // array:string
|
|
4347
|
+
}
|
|
4348
|
+
else {
|
|
4349
|
+
childTypeSchema = field.type[fieldType];
|
|
4350
|
+
}
|
|
4351
|
+
}
|
|
4352
|
+
reflectionField.referencedType = (childTypeSchema)
|
|
4353
|
+
? context.getTypeId(childTypeSchema)
|
|
4354
|
+
: -1;
|
|
4355
|
+
}
|
|
4356
|
+
reflectionField.type = fieldType;
|
|
4357
|
+
type.fields.push(reflectionField);
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4360
|
+
addType(type);
|
|
4361
|
+
});
|
|
4362
|
+
// in case there are types that were not added due to inheritance
|
|
4363
|
+
for (const typeid in pendingReflectionTypes) {
|
|
4364
|
+
pendingReflectionTypes[typeid].forEach((type) => reflection.types.push(type));
|
|
4330
4365
|
}
|
|
4331
4366
|
const buf = reflectionEncoder.encodeAll(it);
|
|
4332
4367
|
return Buffer.from(buf, 0, it.offset);
|
|
@@ -4695,7 +4730,7 @@ class StateView {
|
|
|
4695
4730
|
}
|
|
4696
4731
|
// TODO: allow to set multiple tags at once
|
|
4697
4732
|
add(obj, tag = DEFAULT_VIEW_TAG, checkIncludeParent = true) {
|
|
4698
|
-
if (!obj[$changes]) {
|
|
4733
|
+
if (!obj?.[$changes]) {
|
|
4699
4734
|
console.warn("StateView#add(), invalid object:", obj);
|
|
4700
4735
|
return this;
|
|
4701
4736
|
}
|
|
@@ -4817,7 +4852,7 @@ class StateView {
|
|
|
4817
4852
|
}
|
|
4818
4853
|
this.items.delete(changeTree);
|
|
4819
4854
|
const ref = changeTree.ref;
|
|
4820
|
-
const metadata = ref.constructor[Symbol.metadata];
|
|
4855
|
+
const metadata = ref.constructor[Symbol.metadata]; // ArraySchema/MapSchema do not have metadata
|
|
4821
4856
|
let changes = this.changes.get(changeTree.refId);
|
|
4822
4857
|
if (changes === undefined) {
|
|
4823
4858
|
changes = {};
|
|
@@ -4838,12 +4873,12 @@ class StateView {
|
|
|
4838
4873
|
}
|
|
4839
4874
|
else {
|
|
4840
4875
|
// delete all "tagged" properties.
|
|
4841
|
-
metadata[$viewFieldIndexes].forEach((index) => changes[index] = exports.OPERATION.DELETE);
|
|
4876
|
+
metadata?.[$viewFieldIndexes].forEach((index) => changes[index] = exports.OPERATION.DELETE);
|
|
4842
4877
|
}
|
|
4843
4878
|
}
|
|
4844
4879
|
else {
|
|
4845
4880
|
// delete only tagged properties
|
|
4846
|
-
metadata[$fieldIndexesByViewTag][tag].forEach((index) => changes[index] = exports.OPERATION.DELETE);
|
|
4881
|
+
metadata?.[$fieldIndexesByViewTag][tag].forEach((index) => changes[index] = exports.OPERATION.DELETE);
|
|
4847
4882
|
}
|
|
4848
4883
|
// remove tag
|
|
4849
4884
|
if (this.tags && this.tags.has(changeTree)) {
|