@colyseus/schema 3.0.0-alpha.3 → 3.0.0-alpha.30
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/README.md +131 -61
- package/build/cjs/index.js +644 -283
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +643 -282
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +644 -283
- package/lib/Metadata.d.ts +2 -0
- package/lib/Metadata.js +39 -0
- package/lib/Metadata.js.map +1 -1
- package/lib/Reflection.d.ts +2 -3
- package/lib/Reflection.js +31 -26
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.d.ts +2 -2
- package/lib/Schema.js +2 -2
- package/lib/Schema.js.map +1 -1
- package/lib/annotations.d.ts +0 -19
- package/lib/annotations.js +15 -101
- package/lib/annotations.js.map +1 -1
- package/lib/bench_encode.d.ts +1 -0
- package/lib/bench_encode.js +120 -0
- package/lib/bench_encode.js.map +1 -0
- package/lib/codegen/api.js +1 -2
- package/lib/codegen/api.js.map +1 -1
- package/lib/codegen/languages/cpp.js +1 -2
- package/lib/codegen/languages/cpp.js.map +1 -1
- package/lib/codegen/languages/csharp.js +1 -2
- package/lib/codegen/languages/csharp.js.map +1 -1
- package/lib/codegen/languages/haxe.js +1 -2
- package/lib/codegen/languages/haxe.js.map +1 -1
- package/lib/codegen/languages/java.js +1 -2
- package/lib/codegen/languages/java.js.map +1 -1
- package/lib/codegen/languages/js.js +1 -2
- package/lib/codegen/languages/js.js.map +1 -1
- package/lib/codegen/languages/lua.js +1 -2
- package/lib/codegen/languages/lua.js.map +1 -1
- package/lib/codegen/languages/ts.js +1 -2
- package/lib/codegen/languages/ts.js.map +1 -1
- package/lib/codegen/parser.js +2 -3
- package/lib/codegen/parser.js.map +1 -1
- package/lib/codegen/types.js +3 -3
- package/lib/codegen/types.js.map +1 -1
- package/lib/debug.d.ts +1 -0
- package/lib/debug.js +52 -0
- package/lib/debug.js.map +1 -0
- package/lib/decoder/DecodeOperation.d.ts +0 -1
- package/lib/decoder/DecodeOperation.js +23 -7
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/decoder/Decoder.d.ts +6 -7
- package/lib/decoder/Decoder.js +8 -8
- package/lib/decoder/Decoder.js.map +1 -1
- package/lib/decoder/strategy/RawChanges.js +1 -2
- package/lib/decoder/strategy/RawChanges.js.map +1 -1
- package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
- package/lib/decoder/strategy/StateCallbacks.js +72 -63
- package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
- package/lib/encoder/ChangeTree.d.ts +1 -12
- package/lib/encoder/ChangeTree.js +29 -58
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/EncodeOperation.d.ts +0 -1
- package/lib/encoder/EncodeOperation.js +29 -13
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +10 -8
- package/lib/encoder/Encoder.js +78 -53
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +17 -0
- package/lib/encoder/Root.js +44 -0
- package/lib/encoder/Root.js.map +1 -0
- package/lib/encoder/StateView.d.ts +2 -2
- package/lib/encoder/StateView.js +48 -58
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/encoding/assert.js +3 -3
- package/lib/encoding/assert.js.map +1 -1
- package/lib/encoding/decode.js +21 -22
- package/lib/encoding/decode.js.map +1 -1
- package/lib/encoding/encode.d.ts +2 -2
- package/lib/encoding/encode.js +40 -39
- package/lib/encoding/encode.js.map +1 -1
- package/lib/encoding/spec.d.ts +2 -1
- package/lib/encoding/spec.js +1 -0
- package/lib/encoding/spec.js.map +1 -1
- package/lib/index.d.ts +5 -1
- package/lib/index.js +8 -4
- package/lib/index.js.map +1 -1
- package/lib/types/TypeContext.d.ts +23 -0
- package/lib/types/TypeContext.js +109 -0
- package/lib/types/TypeContext.js.map +1 -0
- package/lib/types/custom/ArraySchema.d.ts +2 -2
- package/lib/types/custom/ArraySchema.js +0 -9
- package/lib/types/custom/ArraySchema.js.map +1 -1
- package/lib/types/registry.js +3 -4
- package/lib/types/registry.js.map +1 -1
- package/lib/types/utils.js +1 -2
- package/lib/types/utils.js.map +1 -1
- package/lib/utils.js +3 -4
- package/lib/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/Metadata.ts +47 -0
- package/src/Reflection.ts +34 -26
- package/src/Schema.ts +2 -2
- package/src/annotations.ts +7 -109
- package/src/bench_encode.ts +97 -0
- package/src/debug.ts +56 -0
- package/src/decoder/DecodeOperation.ts +30 -7
- package/src/decoder/Decoder.ts +13 -11
- package/src/decoder/strategy/StateCallbacks.ts +149 -79
- package/src/encoder/ChangeTree.ts +36 -66
- package/src/encoder/EncodeOperation.ts +29 -12
- package/src/encoder/Encoder.ts +95 -61
- package/src/encoder/Root.ts +51 -0
- package/src/encoder/StateView.ts +51 -67
- package/src/encoding/decode.ts +1 -2
- package/src/encoding/encode.ts +25 -22
- package/src/encoding/spec.ts +1 -0
- package/src/index.ts +8 -11
- package/src/types/TypeContext.ts +127 -0
- package/src/types/custom/ArraySchema.ts +2 -2
package/build/esm/index.mjs
CHANGED
|
@@ -23,6 +23,7 @@ var OPERATION;
|
|
|
23
23
|
OPERATION[OPERATION["REVERSE"] = 15] = "REVERSE";
|
|
24
24
|
OPERATION[OPERATION["MOVE"] = 32] = "MOVE";
|
|
25
25
|
OPERATION[OPERATION["DELETE_BY_REFID"] = 33] = "DELETE_BY_REFID";
|
|
26
|
+
OPERATION[OPERATION["ADD_BY_REFID"] = 129] = "ADD_BY_REFID";
|
|
26
27
|
})(OPERATION || (OPERATION = {}));
|
|
27
28
|
|
|
28
29
|
Symbol.metadata ??= Symbol.for("Symbol.metadata");
|
|
@@ -139,6 +140,45 @@ const Metadata = {
|
|
|
139
140
|
isDeprecated(metadata, field) {
|
|
140
141
|
return metadata[field].deprecated === true;
|
|
141
142
|
},
|
|
143
|
+
init(klass) {
|
|
144
|
+
//
|
|
145
|
+
// Used only to initialize an empty Schema (Encoder#constructor)
|
|
146
|
+
// TODO: remove/refactor this...
|
|
147
|
+
//
|
|
148
|
+
const metadata = {};
|
|
149
|
+
klass[Symbol.metadata] = metadata;
|
|
150
|
+
Object.defineProperty(metadata, -1, {
|
|
151
|
+
value: 0,
|
|
152
|
+
enumerable: false,
|
|
153
|
+
configurable: true,
|
|
154
|
+
});
|
|
155
|
+
},
|
|
156
|
+
initialize(constructor, parentMetadata) {
|
|
157
|
+
let metadata = constructor[Symbol.metadata] ?? Object.create(null);
|
|
158
|
+
// make sure inherited classes have their own metadata object.
|
|
159
|
+
if (constructor[Symbol.metadata] === parentMetadata) {
|
|
160
|
+
metadata = Object.create(null);
|
|
161
|
+
if (parentMetadata) {
|
|
162
|
+
// assign parent metadata to current
|
|
163
|
+
Object.assign(metadata, parentMetadata);
|
|
164
|
+
for (let i = 0; i <= parentMetadata[-1]; i++) {
|
|
165
|
+
Object.defineProperty(metadata, i, {
|
|
166
|
+
value: parentMetadata[i],
|
|
167
|
+
enumerable: false,
|
|
168
|
+
configurable: true,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
Object.defineProperty(metadata, -1, {
|
|
172
|
+
value: parentMetadata[-1],
|
|
173
|
+
enumerable: false,
|
|
174
|
+
configurable: true,
|
|
175
|
+
writable: true,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
constructor[Symbol.metadata] = metadata;
|
|
180
|
+
return metadata;
|
|
181
|
+
},
|
|
142
182
|
isValidInstance(klass) {
|
|
143
183
|
return (klass.constructor[Symbol.metadata] &&
|
|
144
184
|
Object.prototype.hasOwnProperty.call(klass.constructor[Symbol.metadata], -1));
|
|
@@ -154,44 +194,6 @@ const Metadata = {
|
|
|
154
194
|
};
|
|
155
195
|
|
|
156
196
|
var _a$5;
|
|
157
|
-
class Root {
|
|
158
|
-
constructor() {
|
|
159
|
-
this.nextUniqueId = 0;
|
|
160
|
-
this.refCount = new WeakMap();
|
|
161
|
-
// all changes
|
|
162
|
-
this.allChanges = new Map();
|
|
163
|
-
this.allFilteredChanges = new Map();
|
|
164
|
-
// pending changes to be encoded
|
|
165
|
-
this.changes = new Map();
|
|
166
|
-
this.filteredChanges = new Map();
|
|
167
|
-
}
|
|
168
|
-
getNextUniqueId() {
|
|
169
|
-
return this.nextUniqueId++;
|
|
170
|
-
}
|
|
171
|
-
add(changeTree) {
|
|
172
|
-
const refCount = this.refCount.get(changeTree) || 0;
|
|
173
|
-
this.refCount.set(changeTree, refCount + 1);
|
|
174
|
-
}
|
|
175
|
-
remove(changeTree) {
|
|
176
|
-
const refCount = this.refCount.get(changeTree);
|
|
177
|
-
if (refCount <= 1) {
|
|
178
|
-
this.allChanges.delete(changeTree);
|
|
179
|
-
this.changes.delete(changeTree);
|
|
180
|
-
if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
|
|
181
|
-
this.allFilteredChanges.delete(changeTree);
|
|
182
|
-
this.filteredChanges.delete(changeTree);
|
|
183
|
-
}
|
|
184
|
-
this.refCount.delete(changeTree);
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
this.refCount.set(changeTree, refCount - 1);
|
|
188
|
-
}
|
|
189
|
-
changeTree.forEachChild((child, _) => this.remove(child));
|
|
190
|
-
}
|
|
191
|
-
clear() {
|
|
192
|
-
this.changes.clear();
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
197
|
class ChangeTree {
|
|
196
198
|
static { _a$5 = $isNew; }
|
|
197
199
|
;
|
|
@@ -223,8 +225,6 @@ class ChangeTree {
|
|
|
223
225
|
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
224
226
|
this.root.allFilteredChanges.set(this, this.allFilteredChanges);
|
|
225
227
|
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
226
|
-
// } else {
|
|
227
|
-
// this.root.allChanges.set(this, this.allChanges);
|
|
228
228
|
}
|
|
229
229
|
if (!this.isFiltered) {
|
|
230
230
|
this.root.allChanges.set(this, this.allChanges);
|
|
@@ -258,14 +258,12 @@ class ChangeTree {
|
|
|
258
258
|
this.checkIsFiltered(parent, parentIndex);
|
|
259
259
|
if (!this.isFiltered) {
|
|
260
260
|
this.root.changes.set(this, this.changes);
|
|
261
|
+
this.root.allChanges.set(this, this.allChanges);
|
|
261
262
|
}
|
|
262
263
|
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
263
264
|
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
264
265
|
this.root.allFilteredChanges.set(this, this.filteredChanges);
|
|
265
266
|
}
|
|
266
|
-
else {
|
|
267
|
-
this.root.allChanges.set(this, this.allChanges);
|
|
268
|
-
}
|
|
269
267
|
this.ensureRefId();
|
|
270
268
|
this.forEachChild((changeTree, atIndex) => {
|
|
271
269
|
changeTree.setParent(this.ref, root, atIndex);
|
|
@@ -289,7 +287,7 @@ class ChangeTree {
|
|
|
289
287
|
// MapSchema / ArraySchema, etc.
|
|
290
288
|
this.ref.forEach((value, key) => {
|
|
291
289
|
if (Metadata.isValidInstance(value)) {
|
|
292
|
-
callback(value[$changes], this.ref[$changes].indexes[key]);
|
|
290
|
+
callback(value[$changes], this.ref[$changes].indexes[key] ?? key);
|
|
293
291
|
}
|
|
294
292
|
});
|
|
295
293
|
}
|
|
@@ -317,8 +315,9 @@ class ChangeTree {
|
|
|
317
315
|
// TODO: are DELETE operations being encoded as ADD here ??
|
|
318
316
|
//
|
|
319
317
|
if (isFiltered) {
|
|
320
|
-
this.allFilteredChanges.set(index, OPERATION.ADD);
|
|
321
318
|
this.root?.filteredChanges.set(this, this.filteredChanges);
|
|
319
|
+
this.allFilteredChanges.set(index, OPERATION.ADD);
|
|
320
|
+
this.root?.allFilteredChanges.set(this, this.allFilteredChanges);
|
|
322
321
|
}
|
|
323
322
|
else {
|
|
324
323
|
this.allChanges.set(index, OPERATION.ADD);
|
|
@@ -357,7 +356,6 @@ class ChangeTree {
|
|
|
357
356
|
}
|
|
358
357
|
_shiftAllChangeIndexes(shiftIndex, startIndex = 0, allChangeSet) {
|
|
359
358
|
Array.from(allChangeSet.entries()).forEach(([index, op]) => {
|
|
360
|
-
// console.log('shiftAllChangeIndexes', index >= startIndex, { index, op, shiftIndex, startIndex })
|
|
361
359
|
if (index >= startIndex) {
|
|
362
360
|
allChangeSet.delete(index);
|
|
363
361
|
allChangeSet.set(index + shiftIndex, op);
|
|
@@ -498,16 +496,31 @@ class ChangeTree {
|
|
|
498
496
|
}
|
|
499
497
|
checkIsFiltered(parent, parentIndex) {
|
|
500
498
|
// Detect if current structure has "filters" declared
|
|
501
|
-
this.isPartiallyFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-2];
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
499
|
+
this.isPartiallyFiltered = (this.ref['constructor']?.[Symbol.metadata]?.[-2] !== undefined);
|
|
500
|
+
if (parent && !Metadata.isValidInstance(parent)) {
|
|
501
|
+
const parentChangeTree = parent[$changes];
|
|
502
|
+
parent = parentChangeTree.parent;
|
|
503
|
+
parentIndex = parentChangeTree.parentIndex;
|
|
504
|
+
}
|
|
505
|
+
const parentMetadata = parent?.['constructor']?.[Symbol.metadata];
|
|
506
|
+
this.isFiltered = (parent &&
|
|
507
|
+
parentMetadata?.[-2]?.includes(parentIndex));
|
|
508
|
+
// this.isFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-4];
|
|
509
|
+
// // Detect if parent has "filters" declared
|
|
510
|
+
// while (parent && !this.isFiltered) {
|
|
511
|
+
// const metadata: Metadata = parent['constructor'][Symbol.metadata];
|
|
512
|
+
// // this.isFiltered = metadata?.[-4];
|
|
513
|
+
// const fieldName = metadata?.[parentIndex];
|
|
514
|
+
// const isParentOwned = metadata?.[fieldName]?.tag !== undefined;
|
|
515
|
+
// this.isFiltered = isParentOwned || parent[$changes].isFiltered; // metadata?.[-2]
|
|
516
|
+
// parent = parent[$changes].parent;
|
|
517
|
+
// };
|
|
518
|
+
// console.log("ChangeTree.checkIsFiltered", {
|
|
519
|
+
// parent: parent?.constructor.name,
|
|
520
|
+
// ref: this.ref.constructor.name,
|
|
521
|
+
// isFiltered: this.isFiltered,
|
|
522
|
+
// isPartiallyFiltered: this.isPartiallyFiltered,
|
|
523
|
+
// });
|
|
511
524
|
//
|
|
512
525
|
// TODO: refactor this!
|
|
513
526
|
//
|
|
@@ -559,26 +572,29 @@ try {
|
|
|
559
572
|
textEncoder = new TextEncoder();
|
|
560
573
|
}
|
|
561
574
|
catch (e) { }
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
575
|
+
const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength);
|
|
576
|
+
const utf8Length = (hasBufferByteLength)
|
|
577
|
+
? Buffer.byteLength // node
|
|
578
|
+
: function (str, _) {
|
|
579
|
+
var c = 0, length = 0;
|
|
580
|
+
for (var i = 0, l = str.length; i < l; i++) {
|
|
581
|
+
c = str.charCodeAt(i);
|
|
582
|
+
if (c < 0x80) {
|
|
583
|
+
length += 1;
|
|
584
|
+
}
|
|
585
|
+
else if (c < 0x800) {
|
|
586
|
+
length += 2;
|
|
587
|
+
}
|
|
588
|
+
else if (c < 0xd800 || c >= 0xe000) {
|
|
589
|
+
length += 3;
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
i++;
|
|
593
|
+
length += 4;
|
|
594
|
+
}
|
|
578
595
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}
|
|
596
|
+
return length;
|
|
597
|
+
};
|
|
582
598
|
function utf8Write(view, str, it) {
|
|
583
599
|
var c = 0;
|
|
584
600
|
for (var i = 0, l = str.length; i < l; i++) {
|
|
@@ -673,8 +689,7 @@ function string$1(bytes, value, it) {
|
|
|
673
689
|
if (!value) {
|
|
674
690
|
value = "";
|
|
675
691
|
}
|
|
676
|
-
|
|
677
|
-
let length = Buffer.byteLength(value, "utf8");
|
|
692
|
+
let length = utf8Length(value, "utf8");
|
|
678
693
|
let size = 0;
|
|
679
694
|
// fixstr
|
|
680
695
|
if (length < 0x20) {
|
|
@@ -785,23 +800,23 @@ function number$1(bytes, value, it) {
|
|
|
785
800
|
|
|
786
801
|
var encode = /*#__PURE__*/Object.freeze({
|
|
787
802
|
__proto__: null,
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
uint8: uint8$1,
|
|
803
|
+
boolean: boolean$1,
|
|
804
|
+
float32: float32$1,
|
|
805
|
+
float64: float64$1,
|
|
792
806
|
int16: int16$1,
|
|
793
|
-
uint16: uint16$1,
|
|
794
807
|
int32: int32$1,
|
|
795
|
-
uint32: uint32$1,
|
|
796
808
|
int64: int64$1,
|
|
809
|
+
int8: int8$1,
|
|
810
|
+
number: number$1,
|
|
811
|
+
string: string$1,
|
|
812
|
+
uint16: uint16$1,
|
|
813
|
+
uint32: uint32$1,
|
|
797
814
|
uint64: uint64$1,
|
|
798
|
-
|
|
799
|
-
|
|
815
|
+
uint8: uint8$1,
|
|
816
|
+
utf8Length: utf8Length,
|
|
817
|
+
utf8Write: utf8Write,
|
|
800
818
|
writeFloat32: writeFloat32,
|
|
801
|
-
writeFloat64: writeFloat64
|
|
802
|
-
boolean: boolean$1,
|
|
803
|
-
string: string$1,
|
|
804
|
-
number: number$1
|
|
819
|
+
writeFloat64: writeFloat64
|
|
805
820
|
});
|
|
806
821
|
|
|
807
822
|
class EncodeSchemaError extends Error {
|
|
@@ -943,6 +958,18 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
|
|
|
943
958
|
}
|
|
944
959
|
const type = changeTree.getType(field);
|
|
945
960
|
const value = changeTree.getValue(field);
|
|
961
|
+
// try { throw new Error(); } catch (e) {
|
|
962
|
+
// // only print if not coming from Reflection.ts
|
|
963
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
964
|
+
// console.log("encodeKeyValueOperation -> ", {
|
|
965
|
+
// ref: changeTree.ref.constructor.name,
|
|
966
|
+
// field,
|
|
967
|
+
// operation: OPERATION[operation],
|
|
968
|
+
// value: value?.toJSON(),
|
|
969
|
+
// items: ref.toJSON(),
|
|
970
|
+
// });
|
|
971
|
+
// }
|
|
972
|
+
// }
|
|
946
973
|
// TODO: inline this function call small performance gain
|
|
947
974
|
encodeValue(encoder, bytes, ref, type, value, field, operation, it);
|
|
948
975
|
};
|
|
@@ -952,15 +979,19 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
|
|
|
952
979
|
*/
|
|
953
980
|
const encodeArray = function (encoder, bytes, changeTree, field, operation, it, isEncodeAll, hasView) {
|
|
954
981
|
const ref = changeTree.ref;
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
982
|
+
const useOperationByRefId = hasView && changeTree.isFiltered && (typeof (changeTree.getType(field)) !== "string");
|
|
983
|
+
let refOrIndex;
|
|
984
|
+
if (useOperationByRefId) {
|
|
985
|
+
refOrIndex = ref['tmpItems'][field][$changes].refId;
|
|
986
|
+
if (operation === OPERATION.DELETE) {
|
|
987
|
+
operation = OPERATION.DELETE_BY_REFID;
|
|
988
|
+
}
|
|
989
|
+
else if (operation === OPERATION.ADD) {
|
|
990
|
+
operation = OPERATION.ADD_BY_REFID;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
else {
|
|
994
|
+
refOrIndex = field;
|
|
964
995
|
}
|
|
965
996
|
// encode operation
|
|
966
997
|
bytes[it.offset++] = operation & 255;
|
|
@@ -969,7 +1000,7 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
969
1000
|
return;
|
|
970
1001
|
}
|
|
971
1002
|
// encode index
|
|
972
|
-
number$1(bytes,
|
|
1003
|
+
number$1(bytes, refOrIndex, it);
|
|
973
1004
|
// Do not encode value for DELETE operations
|
|
974
1005
|
if (operation === OPERATION.DELETE) {
|
|
975
1006
|
return;
|
|
@@ -1010,7 +1041,6 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
1010
1041
|
* SOFTWARE
|
|
1011
1042
|
*/
|
|
1012
1043
|
function utf8Read(bytes, it, length) {
|
|
1013
|
-
it.offset += length;
|
|
1014
1044
|
var string = '', chr = 0;
|
|
1015
1045
|
for (var i = it.offset, end = it.offset + length; i < end; i++) {
|
|
1016
1046
|
var byte = bytes[i];
|
|
@@ -1047,6 +1077,7 @@ function utf8Read(bytes, it, length) {
|
|
|
1047
1077
|
// (do not throw error to avoid server/client from crashing due to hack attemps)
|
|
1048
1078
|
// throw new Error('Invalid byte ' + byte.toString(16));
|
|
1049
1079
|
}
|
|
1080
|
+
it.offset += length;
|
|
1050
1081
|
return string;
|
|
1051
1082
|
}
|
|
1052
1083
|
function int8(bytes, it) {
|
|
@@ -1218,31 +1249,31 @@ function switchStructureCheck(bytes, it) {
|
|
|
1218
1249
|
|
|
1219
1250
|
var decode = /*#__PURE__*/Object.freeze({
|
|
1220
1251
|
__proto__: null,
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
uint8: uint8,
|
|
1224
|
-
int16: int16,
|
|
1225
|
-
uint16: uint16,
|
|
1226
|
-
int32: int32,
|
|
1227
|
-
uint32: uint32,
|
|
1252
|
+
arrayCheck: arrayCheck,
|
|
1253
|
+
boolean: boolean,
|
|
1228
1254
|
float32: float32,
|
|
1229
1255
|
float64: float64,
|
|
1256
|
+
int16: int16,
|
|
1257
|
+
int32: int32,
|
|
1230
1258
|
int64: int64,
|
|
1231
|
-
|
|
1259
|
+
int8: int8,
|
|
1260
|
+
number: number,
|
|
1261
|
+
numberCheck: numberCheck,
|
|
1232
1262
|
readFloat32: readFloat32,
|
|
1233
1263
|
readFloat64: readFloat64,
|
|
1234
|
-
boolean: boolean,
|
|
1235
1264
|
string: string,
|
|
1236
1265
|
stringCheck: stringCheck,
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1266
|
+
switchStructureCheck: switchStructureCheck,
|
|
1267
|
+
uint16: uint16,
|
|
1268
|
+
uint32: uint32,
|
|
1269
|
+
uint64: uint64,
|
|
1270
|
+
uint8: uint8,
|
|
1271
|
+
utf8Read: utf8Read
|
|
1241
1272
|
});
|
|
1242
1273
|
|
|
1243
1274
|
const DEFINITION_MISMATCH = -1;
|
|
1244
1275
|
function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges) {
|
|
1245
|
-
const $root = decoder
|
|
1276
|
+
const $root = decoder.root;
|
|
1246
1277
|
const previousValue = ref[$getByIndex](index);
|
|
1247
1278
|
let value;
|
|
1248
1279
|
if ((operation & OPERATION.DELETE) === OPERATION.DELETE) {
|
|
@@ -1348,6 +1379,7 @@ const decodeSchemaOperation = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1348
1379
|
// skip early if field is not defined
|
|
1349
1380
|
const field = metadata[index];
|
|
1350
1381
|
if (field === undefined) {
|
|
1382
|
+
console.warn("@colyseus/schema: field not defined at", { index, ref: ref.constructor.name, metadata });
|
|
1351
1383
|
return DEFINITION_MISMATCH;
|
|
1352
1384
|
}
|
|
1353
1385
|
const { value, previousValue } = decodeValue(decoder, operation, ref, index, metadata[field].type, bytes, it, allChanges);
|
|
@@ -1428,7 +1460,8 @@ const decodeKeyValueOperation = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1428
1460
|
};
|
|
1429
1461
|
const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
1430
1462
|
// "uncompressed" index + operation (array/map items)
|
|
1431
|
-
|
|
1463
|
+
let operation = bytes[it.offset++];
|
|
1464
|
+
let index;
|
|
1432
1465
|
if (operation === OPERATION.CLEAR) {
|
|
1433
1466
|
//
|
|
1434
1467
|
// When decoding:
|
|
@@ -1442,8 +1475,8 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1442
1475
|
else if (operation === OPERATION.DELETE_BY_REFID) {
|
|
1443
1476
|
// TODO: refactor here, try to follow same flow as below
|
|
1444
1477
|
const refId = number(bytes, it);
|
|
1445
|
-
const previousValue = decoder
|
|
1446
|
-
|
|
1478
|
+
const previousValue = decoder.root.refs.get(refId);
|
|
1479
|
+
index = ref.findIndex((value) => value === previousValue);
|
|
1447
1480
|
ref[$deleteByIndex](index);
|
|
1448
1481
|
allChanges.push({
|
|
1449
1482
|
ref,
|
|
@@ -1456,7 +1489,18 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1456
1489
|
});
|
|
1457
1490
|
return;
|
|
1458
1491
|
}
|
|
1459
|
-
|
|
1492
|
+
else if (operation === OPERATION.ADD_BY_REFID) {
|
|
1493
|
+
// operation = OPERATION.ADD;
|
|
1494
|
+
const refId = number(bytes, it);
|
|
1495
|
+
const itemByRefId = decoder.root.refs.get(refId);
|
|
1496
|
+
// use existing index, or push new value
|
|
1497
|
+
index = (itemByRefId)
|
|
1498
|
+
? ref.findIndex((value) => value === itemByRefId)
|
|
1499
|
+
: ref.length;
|
|
1500
|
+
}
|
|
1501
|
+
else {
|
|
1502
|
+
index = number(bytes, it);
|
|
1503
|
+
}
|
|
1460
1504
|
const type = ref[$childType];
|
|
1461
1505
|
let dynamicIndex = index;
|
|
1462
1506
|
const { value, previousValue } = decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges);
|
|
@@ -1617,7 +1661,6 @@ class ArraySchema {
|
|
|
1617
1661
|
}
|
|
1618
1662
|
const changeTree = this[$changes];
|
|
1619
1663
|
changeTree.indexedOperation(length, OPERATION.ADD, this.items.length);
|
|
1620
|
-
// changeTree.indexes[length] = length;
|
|
1621
1664
|
this.items.push(value);
|
|
1622
1665
|
this.tmpItems.push(value);
|
|
1623
1666
|
//
|
|
@@ -1865,14 +1908,6 @@ class ArraySchema {
|
|
|
1865
1908
|
lastIndexOf(searchElement, fromIndex = this.length - 1) {
|
|
1866
1909
|
return this.items.lastIndexOf(searchElement, fromIndex);
|
|
1867
1910
|
}
|
|
1868
|
-
/**
|
|
1869
|
-
* Determines whether all the members of an array satisfy the specified test.
|
|
1870
|
-
* @param callbackfn A function that accepts up to three arguments. The every method calls
|
|
1871
|
-
* the callbackfn function for each element in the array until the callbackfn returns a value
|
|
1872
|
-
* which is coercible to the Boolean value false, or until the end of the array.
|
|
1873
|
-
* @param thisArg An object to which the this keyword can refer in the callbackfn function.
|
|
1874
|
-
* If thisArg is omitted, undefined is used as the this value.
|
|
1875
|
-
*/
|
|
1876
1911
|
every(callbackfn, thisArg) {
|
|
1877
1912
|
return this.items.every(callbackfn, thisArg);
|
|
1878
1913
|
}
|
|
@@ -2316,7 +2351,6 @@ class MapSchema {
|
|
|
2316
2351
|
}
|
|
2317
2352
|
registerType("map", { constructor: MapSchema });
|
|
2318
2353
|
|
|
2319
|
-
const DEFAULT_VIEW_TAG = -1;
|
|
2320
2354
|
class TypeContext {
|
|
2321
2355
|
/**
|
|
2322
2356
|
* For inheritance support
|
|
@@ -2338,6 +2372,7 @@ class TypeContext {
|
|
|
2338
2372
|
this.types = {};
|
|
2339
2373
|
this.schemas = new Map();
|
|
2340
2374
|
this.hasFilters = false;
|
|
2375
|
+
this.parentFiltered = {};
|
|
2341
2376
|
if (rootClass) {
|
|
2342
2377
|
this.discoverTypes(rootClass);
|
|
2343
2378
|
}
|
|
@@ -2354,32 +2389,46 @@ class TypeContext {
|
|
|
2354
2389
|
return false;
|
|
2355
2390
|
}
|
|
2356
2391
|
this.types[typeid] = schema;
|
|
2392
|
+
//
|
|
2393
|
+
// Workaround to allow using an empty Schema (with no `@type()` fields)
|
|
2394
|
+
//
|
|
2395
|
+
if (schema[Symbol.metadata] === undefined) {
|
|
2396
|
+
Metadata.init(schema);
|
|
2397
|
+
}
|
|
2357
2398
|
this.schemas.set(schema, typeid);
|
|
2358
2399
|
return true;
|
|
2359
2400
|
}
|
|
2360
2401
|
getTypeId(klass) {
|
|
2361
2402
|
return this.schemas.get(klass);
|
|
2362
2403
|
}
|
|
2363
|
-
discoverTypes(klass) {
|
|
2404
|
+
discoverTypes(klass, parentIndex, parentFieldViewTag) {
|
|
2364
2405
|
if (!this.add(klass)) {
|
|
2365
2406
|
return;
|
|
2366
2407
|
}
|
|
2367
2408
|
// add classes inherited from this base class
|
|
2368
2409
|
TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
|
|
2369
|
-
this.discoverTypes(child);
|
|
2410
|
+
this.discoverTypes(child, parentIndex, parentFieldViewTag);
|
|
2370
2411
|
});
|
|
2371
|
-
|
|
2372
|
-
if (klass[Symbol.metadata] === undefined) {
|
|
2373
|
-
klass[Symbol.metadata] = {};
|
|
2374
|
-
}
|
|
2375
|
-
// const metadata = Metadata.getFor(klass);
|
|
2376
|
-
const metadata = klass[Symbol.metadata];
|
|
2412
|
+
const metadata = (klass[Symbol.metadata] ??= {});
|
|
2377
2413
|
// if any schema/field has filters, mark "context" as having filters.
|
|
2378
2414
|
if (metadata[-2]) {
|
|
2379
2415
|
this.hasFilters = true;
|
|
2380
2416
|
}
|
|
2417
|
+
if (parentFieldViewTag !== undefined) {
|
|
2418
|
+
this.parentFiltered[`${this.schemas.get(klass)}-${parentIndex}`] = true;
|
|
2419
|
+
}
|
|
2381
2420
|
for (const field in metadata) {
|
|
2421
|
+
// //
|
|
2422
|
+
// // Modify the field's metadata to include the parent field's view tag
|
|
2423
|
+
// //
|
|
2424
|
+
// if (
|
|
2425
|
+
// parentFieldViewTag !== undefined &&
|
|
2426
|
+
// metadata[field].tag === undefined
|
|
2427
|
+
// ) {
|
|
2428
|
+
// metadata[field].tag = parentFieldViewTag;
|
|
2429
|
+
// }
|
|
2382
2430
|
const fieldType = metadata[field].type;
|
|
2431
|
+
const viewTag = metadata[field].tag;
|
|
2383
2432
|
if (typeof (fieldType) === "string") {
|
|
2384
2433
|
continue;
|
|
2385
2434
|
}
|
|
@@ -2388,10 +2437,10 @@ class TypeContext {
|
|
|
2388
2437
|
if (type === "string") {
|
|
2389
2438
|
continue;
|
|
2390
2439
|
}
|
|
2391
|
-
this.discoverTypes(type);
|
|
2440
|
+
this.discoverTypes(type, metadata[field].index, viewTag);
|
|
2392
2441
|
}
|
|
2393
2442
|
else if (typeof (fieldType) === "function") {
|
|
2394
|
-
this.discoverTypes(fieldType);
|
|
2443
|
+
this.discoverTypes(fieldType, viewTag);
|
|
2395
2444
|
}
|
|
2396
2445
|
else {
|
|
2397
2446
|
const type = Object.values(fieldType)[0];
|
|
@@ -2399,11 +2448,13 @@ class TypeContext {
|
|
|
2399
2448
|
if (typeof (type) === "string") {
|
|
2400
2449
|
continue;
|
|
2401
2450
|
}
|
|
2402
|
-
this.discoverTypes(type);
|
|
2451
|
+
this.discoverTypes(type, metadata[field].index, viewTag);
|
|
2403
2452
|
}
|
|
2404
2453
|
}
|
|
2405
2454
|
}
|
|
2406
2455
|
}
|
|
2456
|
+
|
|
2457
|
+
const DEFAULT_VIEW_TAG = -1;
|
|
2407
2458
|
/**
|
|
2408
2459
|
* [See documentation](https://docs.colyseus.io/state/schema/)
|
|
2409
2460
|
*
|
|
@@ -2551,6 +2602,7 @@ function view(tag = DEFAULT_VIEW_TAG) {
|
|
|
2551
2602
|
const constructor = target.constructor;
|
|
2552
2603
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
2553
2604
|
const parentMetadata = parentClass[Symbol.metadata];
|
|
2605
|
+
// TODO: use Metadata.initialize()
|
|
2554
2606
|
const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
|
|
2555
2607
|
if (!metadata[fieldName]) {
|
|
2556
2608
|
//
|
|
@@ -2575,8 +2627,8 @@ function type(type, options) {
|
|
|
2575
2627
|
// for inheritance support
|
|
2576
2628
|
TypeContext.register(constructor);
|
|
2577
2629
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
2578
|
-
const parentMetadata = parentClass[Symbol.metadata];
|
|
2579
|
-
const metadata =
|
|
2630
|
+
const parentMetadata = parentClass && parentClass[Symbol.metadata];
|
|
2631
|
+
const metadata = Metadata.initialize(constructor, parentMetadata);
|
|
2580
2632
|
let fieldIndex;
|
|
2581
2633
|
/**
|
|
2582
2634
|
* skip if descriptor already exists for this field (`@deprecated()`)
|
|
@@ -2957,13 +3009,13 @@ class Schema {
|
|
|
2957
3009
|
}
|
|
2958
3010
|
return output;
|
|
2959
3011
|
}
|
|
2960
|
-
static debugChangesDeep(ref) {
|
|
3012
|
+
static debugChangesDeep(ref, changeSetName = "changes") {
|
|
2961
3013
|
let output = "";
|
|
2962
3014
|
const rootChangeTree = ref[$changes];
|
|
2963
3015
|
const changeTrees = new Map();
|
|
2964
3016
|
let totalInstances = 0;
|
|
2965
3017
|
let totalOperations = 0;
|
|
2966
|
-
for (const [changeTree, changes] of (rootChangeTree.root.
|
|
3018
|
+
for (const [changeTree, changes] of (rootChangeTree.root[changeSetName].entries())) {
|
|
2967
3019
|
let includeChangeTree = false;
|
|
2968
3020
|
let parentChangeTrees = [];
|
|
2969
3021
|
let parentChangeTree = changeTree.parent?.[$changes];
|
|
@@ -3349,6 +3401,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
3349
3401
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
3350
3402
|
PERFORMANCE OF THIS SOFTWARE.
|
|
3351
3403
|
***************************************************************************** */
|
|
3404
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
3405
|
+
|
|
3352
3406
|
|
|
3353
3407
|
function __decorate(decorators, target, key, desc) {
|
|
3354
3408
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -3362,29 +3416,68 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
3362
3416
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
3363
3417
|
};
|
|
3364
3418
|
|
|
3419
|
+
class Root {
|
|
3420
|
+
constructor(types) {
|
|
3421
|
+
this.types = types;
|
|
3422
|
+
this.nextUniqueId = 0;
|
|
3423
|
+
this.refCount = new WeakMap();
|
|
3424
|
+
// all changes
|
|
3425
|
+
this.allChanges = new Map();
|
|
3426
|
+
this.allFilteredChanges = new Map();
|
|
3427
|
+
// pending changes to be encoded
|
|
3428
|
+
this.changes = new Map();
|
|
3429
|
+
this.filteredChanges = new Map();
|
|
3430
|
+
}
|
|
3431
|
+
getNextUniqueId() {
|
|
3432
|
+
return this.nextUniqueId++;
|
|
3433
|
+
}
|
|
3434
|
+
add(changeTree) {
|
|
3435
|
+
const refCount = this.refCount.get(changeTree) || 0;
|
|
3436
|
+
this.refCount.set(changeTree, refCount + 1);
|
|
3437
|
+
}
|
|
3438
|
+
remove(changeTree) {
|
|
3439
|
+
const refCount = this.refCount.get(changeTree);
|
|
3440
|
+
if (refCount <= 1) {
|
|
3441
|
+
this.allChanges.delete(changeTree);
|
|
3442
|
+
this.changes.delete(changeTree);
|
|
3443
|
+
if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
|
|
3444
|
+
this.allFilteredChanges.delete(changeTree);
|
|
3445
|
+
this.filteredChanges.delete(changeTree);
|
|
3446
|
+
}
|
|
3447
|
+
this.refCount.delete(changeTree);
|
|
3448
|
+
}
|
|
3449
|
+
else {
|
|
3450
|
+
this.refCount.set(changeTree, refCount - 1);
|
|
3451
|
+
}
|
|
3452
|
+
changeTree.forEachChild((child, _) => this.remove(child));
|
|
3453
|
+
}
|
|
3454
|
+
clear() {
|
|
3455
|
+
this.changes.clear();
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
|
|
3365
3459
|
class Encoder {
|
|
3366
3460
|
static { this.BUFFER_SIZE = 8 * 1024; } // 8KB
|
|
3367
|
-
constructor(
|
|
3461
|
+
constructor(state) {
|
|
3368
3462
|
this.sharedBuffer = Buffer.allocUnsafeSlow(Encoder.BUFFER_SIZE);
|
|
3369
|
-
this.setRoot(root);
|
|
3370
3463
|
//
|
|
3371
3464
|
// TODO: cache and restore "Context" based on root schema
|
|
3372
3465
|
// (to avoid creating a new context for every new room)
|
|
3373
3466
|
//
|
|
3374
|
-
this.context = new TypeContext(
|
|
3467
|
+
this.context = new TypeContext(state.constructor);
|
|
3468
|
+
this.root = new Root(this.context);
|
|
3469
|
+
this.setState(state);
|
|
3375
3470
|
// console.log(">>>>>>>>>>>>>>>> Encoder types");
|
|
3376
3471
|
// this.context.schemas.forEach((id, schema) => {
|
|
3377
3472
|
// console.log("type:", id, schema.name, Object.keys(schema[Symbol.metadata]));
|
|
3378
3473
|
// });
|
|
3379
3474
|
}
|
|
3380
|
-
|
|
3381
|
-
this.root = new Root();
|
|
3475
|
+
setState(state) {
|
|
3382
3476
|
this.state = state;
|
|
3383
|
-
state[$changes].setRoot(this.root);
|
|
3477
|
+
this.state[$changes].setRoot(this.root);
|
|
3384
3478
|
}
|
|
3385
|
-
encode(it = { offset: 0 }, view,
|
|
3386
|
-
|
|
3387
|
-
const isEncodeAll = this.root.allChanges === changeTrees;
|
|
3479
|
+
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
|
|
3480
|
+
) {
|
|
3388
3481
|
const hasView = (view !== undefined);
|
|
3389
3482
|
const rootChangeTree = this.state[$changes];
|
|
3390
3483
|
const changeTreesIterator = changeTrees.entries();
|
|
@@ -3393,6 +3486,12 @@ class Encoder {
|
|
|
3393
3486
|
const ctor = ref['constructor'];
|
|
3394
3487
|
const encoder = ctor[$encoder];
|
|
3395
3488
|
const filter = ctor[$filter];
|
|
3489
|
+
// try { throw new Error(); } catch (e) {
|
|
3490
|
+
// // only print if not coming from Reflection.ts
|
|
3491
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
3492
|
+
// console.log("ChangeTree:", { ref: ref.constructor.name, });
|
|
3493
|
+
// }
|
|
3494
|
+
// }
|
|
3396
3495
|
if (hasView) {
|
|
3397
3496
|
if (!view.items.has(changeTree)) {
|
|
3398
3497
|
view.invisible.add(changeTree);
|
|
@@ -3403,9 +3502,10 @@ class Encoder {
|
|
|
3403
3502
|
}
|
|
3404
3503
|
}
|
|
3405
3504
|
// skip root `refId` if it's the first change tree
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3505
|
+
// (unless it "hasView", which will need to revisit the root)
|
|
3506
|
+
if (hasView || it.offset > initialOffset || changeTree !== rootChangeTree) {
|
|
3507
|
+
buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
|
|
3508
|
+
number$1(buffer, changeTree.refId, it);
|
|
3409
3509
|
}
|
|
3410
3510
|
const changesIterator = changes.entries();
|
|
3411
3511
|
for (const [fieldIndex, operation] of changesIterator) {
|
|
@@ -3417,27 +3517,39 @@ class Encoder {
|
|
|
3417
3517
|
// TODO: avoid checking if no view tags were defined
|
|
3418
3518
|
//
|
|
3419
3519
|
if (filter && !filter(ref, fieldIndex, view)) {
|
|
3420
|
-
// console.log("SKIP FIELD:", { ref: changeTree.ref.constructor.name, fieldIndex, })
|
|
3421
3520
|
// console.log("ADD AS INVISIBLE:", fieldIndex, changeTree.ref.constructor.name)
|
|
3422
3521
|
// view?.invisible.add(changeTree);
|
|
3423
3522
|
continue;
|
|
3424
3523
|
}
|
|
3425
|
-
//
|
|
3426
|
-
//
|
|
3427
|
-
//
|
|
3428
|
-
//
|
|
3429
|
-
//
|
|
3430
|
-
|
|
3524
|
+
// try { throw new Error(); } catch (e) {
|
|
3525
|
+
// // only print if not coming from Reflection.ts
|
|
3526
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
3527
|
+
// console.log("WILL ENCODE", {
|
|
3528
|
+
// ref: changeTree.ref.constructor.name,
|
|
3529
|
+
// fieldIndex,
|
|
3530
|
+
// operation: OPERATION[operation],
|
|
3531
|
+
// });
|
|
3532
|
+
// }
|
|
3533
|
+
// }
|
|
3534
|
+
encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
|
|
3431
3535
|
}
|
|
3432
3536
|
}
|
|
3433
|
-
if (it.offset >
|
|
3434
|
-
const newSize = getNextPowerOf2(
|
|
3435
|
-
console.warn(
|
|
3537
|
+
if (it.offset > buffer.byteLength) {
|
|
3538
|
+
const newSize = getNextPowerOf2(buffer.byteLength * 2);
|
|
3539
|
+
console.warn(`@colyseus/schema buffer overflow. Encoded state is higher than default BUFFER_SIZE. Use the following to increase default BUFFER_SIZE:
|
|
3540
|
+
|
|
3541
|
+
import { Encoder } from "@colyseus/schema";
|
|
3542
|
+
Encoder.BUFFER_SIZE = ${Math.round(newSize / 1024)} * 1024; // ${Math.round(newSize / 1024)} KB
|
|
3543
|
+
`);
|
|
3436
3544
|
//
|
|
3437
3545
|
// resize buffer and re-encode (TODO: can we avoid re-encoding here?)
|
|
3438
3546
|
//
|
|
3439
|
-
|
|
3440
|
-
|
|
3547
|
+
buffer = Buffer.allocUnsafeSlow(newSize);
|
|
3548
|
+
// assign resized buffer to local sharedBuffer
|
|
3549
|
+
if (buffer === this.sharedBuffer) {
|
|
3550
|
+
this.sharedBuffer = buffer;
|
|
3551
|
+
}
|
|
3552
|
+
return this.encode({ offset: initialOffset }, view, buffer, changeTrees, isEncodeAll);
|
|
3441
3553
|
}
|
|
3442
3554
|
else {
|
|
3443
3555
|
//
|
|
@@ -3449,42 +3561,47 @@ class Encoder {
|
|
|
3449
3561
|
//
|
|
3450
3562
|
this.onEndEncode(changeTrees);
|
|
3451
3563
|
}
|
|
3452
|
-
|
|
3453
|
-
return bytes.slice(0, it.offset);
|
|
3564
|
+
return buffer.subarray(0, it.offset);
|
|
3454
3565
|
}
|
|
3455
3566
|
}
|
|
3456
|
-
encodeAll(it = { offset: 0 }) {
|
|
3457
|
-
// console.log(
|
|
3458
|
-
//
|
|
3459
|
-
|
|
3460
|
-
// });
|
|
3461
|
-
return this.encode(it, undefined, this.sharedBuffer, this.root.allChanges);
|
|
3567
|
+
encodeAll(it = { offset: 0 }, buffer = this.sharedBuffer) {
|
|
3568
|
+
// console.log(`\nencodeAll(), this.root.allChanges (${this.root.allChanges.size})`);
|
|
3569
|
+
// this.debugChanges("allChanges");
|
|
3570
|
+
return this.encode(it, undefined, buffer, this.root.allChanges, true);
|
|
3462
3571
|
}
|
|
3463
3572
|
encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3464
3573
|
const viewOffset = it.offset;
|
|
3465
|
-
// console.log(
|
|
3466
|
-
// this.
|
|
3574
|
+
// console.log(`\nencodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
|
|
3575
|
+
// this.debugChanges("allFilteredChanges");
|
|
3467
3576
|
// try to encode "filtered" changes
|
|
3468
|
-
this.encode(it, view, bytes, this.root.allFilteredChanges);
|
|
3577
|
+
this.encode(it, view, bytes, this.root.allFilteredChanges, true, viewOffset);
|
|
3469
3578
|
return Buffer.concat([
|
|
3470
|
-
bytes.
|
|
3471
|
-
bytes.
|
|
3579
|
+
bytes.subarray(0, sharedOffset),
|
|
3580
|
+
bytes.subarray(viewOffset, it.offset)
|
|
3472
3581
|
]);
|
|
3473
3582
|
}
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3583
|
+
debugChanges(field) {
|
|
3584
|
+
const changeSet = (typeof (field) === "string")
|
|
3585
|
+
? this.root[field]
|
|
3586
|
+
: field;
|
|
3587
|
+
Array.from(changeSet.entries()).map((item) => {
|
|
3588
|
+
const metadata = item[0].ref.constructor[Symbol.metadata];
|
|
3589
|
+
console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
|
|
3590
|
+
item[1].forEach((op, index) => {
|
|
3591
|
+
console.log(" ->", {
|
|
3592
|
+
index,
|
|
3593
|
+
field: metadata?.[index],
|
|
3594
|
+
op: OPERATION[op],
|
|
3595
|
+
});
|
|
3596
|
+
});
|
|
3597
|
+
});
|
|
3598
|
+
}
|
|
3484
3599
|
encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3485
3600
|
const viewOffset = it.offset;
|
|
3486
|
-
//
|
|
3487
|
-
this.
|
|
3601
|
+
// console.log(`\nencodeView(), view.changes (${view.changes.size})`);
|
|
3602
|
+
// this.debugChanges(view.changes);
|
|
3603
|
+
// console.log(`\nencodeView(), this.root.filteredChanges (${this.root.filteredChanges.size})`);
|
|
3604
|
+
// this.debugChanges("filteredChanges");
|
|
3488
3605
|
// encode visibility changes (add/remove for this view)
|
|
3489
3606
|
const viewChangesIterator = view.changes.entries();
|
|
3490
3607
|
for (const [changeTree, changes] of viewChangesIterator) {
|
|
@@ -3511,9 +3628,11 @@ class Encoder {
|
|
|
3511
3628
|
//
|
|
3512
3629
|
// clear "view" changes after encoding
|
|
3513
3630
|
view.changes.clear();
|
|
3631
|
+
// try to encode "filtered" changes
|
|
3632
|
+
this.encode(it, view, bytes, this.root.filteredChanges, false, viewOffset);
|
|
3514
3633
|
return Buffer.concat([
|
|
3515
|
-
bytes.
|
|
3516
|
-
bytes.
|
|
3634
|
+
bytes.subarray(0, sharedOffset),
|
|
3635
|
+
bytes.subarray(viewOffset, it.offset)
|
|
3517
3636
|
]);
|
|
3518
3637
|
}
|
|
3519
3638
|
onEndEncode(changeTrees = this.root.changes) {
|
|
@@ -3683,21 +3802,21 @@ class ReferenceTracker {
|
|
|
3683
3802
|
class Decoder {
|
|
3684
3803
|
constructor(root, context) {
|
|
3685
3804
|
this.currentRefId = 0;
|
|
3686
|
-
this.
|
|
3805
|
+
this.setState(root);
|
|
3687
3806
|
this.context = context || new TypeContext(root.constructor);
|
|
3688
3807
|
// console.log(">>>>>>>>>>>>>>>> Decoder types");
|
|
3689
3808
|
// this.context.schemas.forEach((id, schema) => {
|
|
3690
3809
|
// console.log("type:", id, schema.name, Object.keys(schema[Symbol.metadata]));
|
|
3691
3810
|
// });
|
|
3692
3811
|
}
|
|
3693
|
-
|
|
3812
|
+
setState(root) {
|
|
3694
3813
|
this.state = root;
|
|
3695
|
-
this
|
|
3696
|
-
this
|
|
3814
|
+
this.root = new ReferenceTracker();
|
|
3815
|
+
this.root.addRef(0, root);
|
|
3697
3816
|
}
|
|
3698
3817
|
decode(bytes, it = { offset: 0 }, ref = this.state) {
|
|
3699
3818
|
const allChanges = [];
|
|
3700
|
-
const $root = this
|
|
3819
|
+
const $root = this.root;
|
|
3701
3820
|
const totalBytes = bytes.byteLength;
|
|
3702
3821
|
let decoder = ref['constructor'][$decoder];
|
|
3703
3822
|
this.currentRefId = 0;
|
|
@@ -3778,7 +3897,7 @@ class Decoder {
|
|
|
3778
3897
|
previousValue: value
|
|
3779
3898
|
});
|
|
3780
3899
|
if (needRemoveRef) {
|
|
3781
|
-
this
|
|
3900
|
+
this.root.removeRef(this.root.refIds.get(value));
|
|
3782
3901
|
}
|
|
3783
3902
|
});
|
|
3784
3903
|
}
|
|
@@ -3818,10 +3937,8 @@ class Reflection extends Schema {
|
|
|
3818
3937
|
super(...arguments);
|
|
3819
3938
|
this.types = new ArraySchema();
|
|
3820
3939
|
}
|
|
3821
|
-
static encode(instance, context) {
|
|
3822
|
-
|
|
3823
|
-
context = new TypeContext(instance.constructor);
|
|
3824
|
-
}
|
|
3940
|
+
static encode(instance, context, it = { offset: 0 }) {
|
|
3941
|
+
context ??= new TypeContext(instance.constructor);
|
|
3825
3942
|
const reflection = new Reflection();
|
|
3826
3943
|
const encoder = new Encoder(reflection);
|
|
3827
3944
|
const buildType = (currentType, metadata) => {
|
|
@@ -3875,7 +3992,6 @@ class Reflection extends Schema {
|
|
|
3875
3992
|
}
|
|
3876
3993
|
buildType(type, klass[Symbol.metadata]);
|
|
3877
3994
|
}
|
|
3878
|
-
const it = { offset: 0 };
|
|
3879
3995
|
const buf = encoder.encodeAll(it);
|
|
3880
3996
|
return Buffer.from(buf, 0, it.offset);
|
|
3881
3997
|
}
|
|
@@ -3883,59 +3999,314 @@ class Reflection extends Schema {
|
|
|
3883
3999
|
const reflection = new Reflection();
|
|
3884
4000
|
const reflectionDecoder = new Decoder(reflection);
|
|
3885
4001
|
reflectionDecoder.decode(bytes, it);
|
|
3886
|
-
const
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
const
|
|
4002
|
+
const typeContext = new TypeContext();
|
|
4003
|
+
// 1st pass, initialize metadata + inheritance
|
|
4004
|
+
reflection.types.forEach((reflectionType) => {
|
|
4005
|
+
const parentClass = typeContext.get(reflectionType.extendsId) ?? Schema;
|
|
4006
|
+
const schema = class _ extends parentClass {
|
|
3890
4007
|
};
|
|
3891
|
-
|
|
3892
|
-
const _metadata = parentKlass && parentKlass[Symbol.metadata] || Object.create(null);
|
|
3893
|
-
Object.defineProperty(schema, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
4008
|
+
const parentMetadata = parentClass[Symbol.metadata];
|
|
3894
4009
|
// register for inheritance support
|
|
3895
4010
|
TypeContext.register(schema);
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
return types;
|
|
4011
|
+
// for inheritance support
|
|
4012
|
+
Metadata.initialize(schema, parentMetadata);
|
|
4013
|
+
typeContext.add(schema, reflectionType.id);
|
|
3900
4014
|
}, {});
|
|
4015
|
+
// 2nd pass, set fields
|
|
3901
4016
|
reflection.types.forEach((reflectionType) => {
|
|
3902
|
-
const schemaType =
|
|
4017
|
+
const schemaType = typeContext.get(reflectionType.id);
|
|
3903
4018
|
const metadata = schemaType[Symbol.metadata];
|
|
3904
|
-
|
|
3905
|
-
const parentFieldIndex =
|
|
4019
|
+
// FIXME: use metadata[-1] to get field count
|
|
4020
|
+
const parentFieldIndex = 0;
|
|
4021
|
+
// console.log("--------------------");
|
|
4022
|
+
// // console.log("reflectionType", reflectionType.toJSON());
|
|
4023
|
+
// console.log("reflectionType.fields", reflectionType.fields.toJSON());
|
|
4024
|
+
// console.log("parentFieldIndex", parentFieldIndex);
|
|
4025
|
+
//
|
|
4026
|
+
// FIXME: set fields using parentKlass as well
|
|
4027
|
+
// currently the fields are duplicated on inherited classes
|
|
4028
|
+
//
|
|
4029
|
+
// // const parentKlass = reflection.types[reflectionType.extendsId];
|
|
4030
|
+
// // parentKlass.fields
|
|
3906
4031
|
reflectionType.fields.forEach((field, i) => {
|
|
3907
4032
|
const fieldIndex = parentFieldIndex + i;
|
|
3908
4033
|
if (field.referencedType !== undefined) {
|
|
3909
4034
|
let fieldType = field.type;
|
|
3910
|
-
let refType =
|
|
4035
|
+
let refType = typeContext.get(field.referencedType);
|
|
3911
4036
|
// map or array of primitive type (-1)
|
|
3912
4037
|
if (!refType) {
|
|
3913
4038
|
const typeInfo = field.type.split(":");
|
|
3914
4039
|
fieldType = typeInfo[0];
|
|
3915
|
-
refType = typeInfo[1];
|
|
4040
|
+
refType = typeInfo[1]; // string
|
|
3916
4041
|
}
|
|
3917
4042
|
if (fieldType === "ref") {
|
|
3918
|
-
// type(refType)(schemaType.prototype, field.name);
|
|
3919
4043
|
Metadata.addField(metadata, fieldIndex, field.name, refType);
|
|
3920
4044
|
}
|
|
3921
4045
|
else {
|
|
3922
|
-
// type({ [fieldType]: refType } as DefinitionType)(schemaType.prototype, field.name);
|
|
3923
4046
|
Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType });
|
|
3924
4047
|
}
|
|
3925
4048
|
}
|
|
3926
4049
|
else {
|
|
3927
|
-
// type(field.type as PrimitiveType)(schemaType.prototype, field.name);
|
|
3928
4050
|
Metadata.addField(metadata, fieldIndex, field.name, field.type);
|
|
3929
4051
|
}
|
|
3930
4052
|
});
|
|
3931
4053
|
});
|
|
3932
|
-
|
|
4054
|
+
// @ts-ignore
|
|
4055
|
+
return new (typeContext.get(0))();
|
|
3933
4056
|
}
|
|
3934
4057
|
}
|
|
3935
4058
|
__decorate([
|
|
3936
4059
|
type([ReflectionType])
|
|
3937
4060
|
], Reflection.prototype, "types", void 0);
|
|
3938
4061
|
|
|
4062
|
+
function getDecoderStateCallbacks(decoder) {
|
|
4063
|
+
const $root = decoder.root;
|
|
4064
|
+
const callbacks = $root.callbacks;
|
|
4065
|
+
const onAddCalls = new WeakMap();
|
|
4066
|
+
let currentOnAddCallback;
|
|
4067
|
+
decoder.triggerChanges = function (allChanges) {
|
|
4068
|
+
const uniqueRefIds = new Set();
|
|
4069
|
+
for (let i = 0, l = allChanges.length; i < l; i++) {
|
|
4070
|
+
const change = allChanges[i];
|
|
4071
|
+
const refId = change.refId;
|
|
4072
|
+
const ref = change.ref;
|
|
4073
|
+
const $callbacks = callbacks[refId];
|
|
4074
|
+
if (!$callbacks) {
|
|
4075
|
+
continue;
|
|
4076
|
+
}
|
|
4077
|
+
//
|
|
4078
|
+
// trigger onRemove on child structure.
|
|
4079
|
+
//
|
|
4080
|
+
if ((change.op & OPERATION.DELETE) === OPERATION.DELETE &&
|
|
4081
|
+
change.previousValue instanceof Schema) {
|
|
4082
|
+
const deleteCallbacks = callbacks[$root.refIds.get(change.previousValue)]?.[OPERATION.DELETE];
|
|
4083
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
4084
|
+
deleteCallbacks[i]();
|
|
4085
|
+
}
|
|
4086
|
+
}
|
|
4087
|
+
if (ref instanceof Schema) {
|
|
4088
|
+
//
|
|
4089
|
+
// Handle schema instance
|
|
4090
|
+
//
|
|
4091
|
+
if (!uniqueRefIds.has(refId)) {
|
|
4092
|
+
// trigger onChange
|
|
4093
|
+
const replaceCallbacks = $callbacks?.[OPERATION.REPLACE];
|
|
4094
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
4095
|
+
replaceCallbacks[i]();
|
|
4096
|
+
// try {
|
|
4097
|
+
// } catch (e) {
|
|
4098
|
+
// console.error(e);
|
|
4099
|
+
// }
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
if ($callbacks.hasOwnProperty(change.field)) {
|
|
4103
|
+
const fieldCallbacks = $callbacks[change.field];
|
|
4104
|
+
for (let i = fieldCallbacks?.length - 1; i >= 0; i--) {
|
|
4105
|
+
fieldCallbacks[i](change.value, change.previousValue);
|
|
4106
|
+
// try {
|
|
4107
|
+
// } catch (e) {
|
|
4108
|
+
// console.error(e);
|
|
4109
|
+
// }
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
else {
|
|
4114
|
+
//
|
|
4115
|
+
// Handle collection of items
|
|
4116
|
+
//
|
|
4117
|
+
if ((change.op & OPERATION.DELETE) === OPERATION.DELETE) {
|
|
4118
|
+
//
|
|
4119
|
+
// FIXME: `previousValue` should always be available.
|
|
4120
|
+
//
|
|
4121
|
+
if (change.previousValue !== undefined) {
|
|
4122
|
+
// triger onRemove
|
|
4123
|
+
const deleteCallbacks = $callbacks[OPERATION.DELETE];
|
|
4124
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
4125
|
+
deleteCallbacks[i](change.previousValue, change.dynamicIndex ?? change.field);
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
// Handle DELETE_AND_ADD operations
|
|
4129
|
+
if ((change.op & OPERATION.ADD) === OPERATION.ADD) {
|
|
4130
|
+
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
4131
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4132
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
}
|
|
4136
|
+
else if ((change.op & OPERATION.ADD) === OPERATION.ADD && change.previousValue === undefined) {
|
|
4137
|
+
// triger onAdd
|
|
4138
|
+
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
4139
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4140
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
// trigger onChange
|
|
4144
|
+
if (change.value !== change.previousValue) {
|
|
4145
|
+
const replaceCallbacks = $callbacks[OPERATION.REPLACE];
|
|
4146
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
4147
|
+
replaceCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4148
|
+
}
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
uniqueRefIds.add(refId);
|
|
4152
|
+
}
|
|
4153
|
+
};
|
|
4154
|
+
function getProxy(metadataOrType, context) {
|
|
4155
|
+
let metadata = context.instance?.constructor[Symbol.metadata] || metadataOrType;
|
|
4156
|
+
let isCollection = ((context.instance && typeof (context.instance['forEach']) === "function") ||
|
|
4157
|
+
(metadataOrType && typeof (metadataOrType[Symbol.metadata]) === "undefined"));
|
|
4158
|
+
if (metadata && !isCollection) {
|
|
4159
|
+
const onAddListen = function (ref, prop, callback, immediate) {
|
|
4160
|
+
// immediate trigger
|
|
4161
|
+
if (immediate &&
|
|
4162
|
+
context.instance[prop] !== undefined &&
|
|
4163
|
+
!onAddCalls.has(currentOnAddCallback) // Workaround for https://github.com/colyseus/schema/issues/147
|
|
4164
|
+
) {
|
|
4165
|
+
callback(context.instance[prop], undefined);
|
|
4166
|
+
}
|
|
4167
|
+
return $root.addCallback($root.refIds.get(ref), prop, callback);
|
|
4168
|
+
};
|
|
4169
|
+
/**
|
|
4170
|
+
* Schema instances
|
|
4171
|
+
*/
|
|
4172
|
+
return new Proxy({
|
|
4173
|
+
listen: function listen(prop, callback, immediate = true) {
|
|
4174
|
+
if (context.instance) {
|
|
4175
|
+
return onAddListen(context.instance, prop, callback, immediate);
|
|
4176
|
+
}
|
|
4177
|
+
else {
|
|
4178
|
+
// collection instance not received yet
|
|
4179
|
+
let detachCallback = () => { };
|
|
4180
|
+
context.onInstanceAvailable((ref, existing) => {
|
|
4181
|
+
detachCallback = onAddListen(ref, prop, callback, immediate && existing && !onAddCalls.has(currentOnAddCallback));
|
|
4182
|
+
});
|
|
4183
|
+
return () => detachCallback();
|
|
4184
|
+
}
|
|
4185
|
+
},
|
|
4186
|
+
onChange: function onChange(callback) {
|
|
4187
|
+
return $root.addCallback($root.refIds.get(context.instance), OPERATION.REPLACE, callback);
|
|
4188
|
+
},
|
|
4189
|
+
//
|
|
4190
|
+
// TODO: refactor `bindTo()` implementation.
|
|
4191
|
+
// There is room for improvement.
|
|
4192
|
+
//
|
|
4193
|
+
bindTo: function bindTo(targetObject, properties) {
|
|
4194
|
+
if (!properties) {
|
|
4195
|
+
properties = Object.keys(metadata);
|
|
4196
|
+
}
|
|
4197
|
+
return $root.addCallback($root.refIds.get(context.instance), OPERATION.REPLACE, () => {
|
|
4198
|
+
properties.forEach((prop) => targetObject[prop] = context.instance[prop]);
|
|
4199
|
+
});
|
|
4200
|
+
}
|
|
4201
|
+
}, {
|
|
4202
|
+
get(target, prop) {
|
|
4203
|
+
if (metadata[prop]) {
|
|
4204
|
+
const instance = context.instance?.[prop];
|
|
4205
|
+
const onInstanceAvailable = ((callback) => {
|
|
4206
|
+
const unbind = $(context.instance).listen(prop, (value, _) => {
|
|
4207
|
+
callback(value, false);
|
|
4208
|
+
// FIXME: by "unbinding" the callback here,
|
|
4209
|
+
// it will not support when the server
|
|
4210
|
+
// re-instantiates the instance.
|
|
4211
|
+
//
|
|
4212
|
+
unbind?.();
|
|
4213
|
+
}, false);
|
|
4214
|
+
// has existing value
|
|
4215
|
+
if ($root.refIds.get(instance) !== undefined) {
|
|
4216
|
+
callback(instance, true);
|
|
4217
|
+
}
|
|
4218
|
+
});
|
|
4219
|
+
return getProxy(metadata[prop].type, {
|
|
4220
|
+
// make sure refId is available, otherwise need to wait for the instance to be available.
|
|
4221
|
+
instance: ($root.refIds.get(instance) && instance),
|
|
4222
|
+
parentInstance: context.instance,
|
|
4223
|
+
onInstanceAvailable,
|
|
4224
|
+
});
|
|
4225
|
+
}
|
|
4226
|
+
else {
|
|
4227
|
+
// accessing the function
|
|
4228
|
+
return target[prop];
|
|
4229
|
+
}
|
|
4230
|
+
},
|
|
4231
|
+
has(target, prop) { return metadata[prop] !== undefined; },
|
|
4232
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4233
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4234
|
+
});
|
|
4235
|
+
}
|
|
4236
|
+
else {
|
|
4237
|
+
/**
|
|
4238
|
+
* Collection instances
|
|
4239
|
+
*/
|
|
4240
|
+
const onAdd = function (ref, callback, immediate) {
|
|
4241
|
+
// Trigger callback on existing items
|
|
4242
|
+
if (immediate) {
|
|
4243
|
+
ref.forEach((v, k) => callback(v, k));
|
|
4244
|
+
}
|
|
4245
|
+
return $root.addCallback($root.refIds.get(ref), OPERATION.ADD, (value, key) => {
|
|
4246
|
+
onAddCalls.set(callback, true);
|
|
4247
|
+
currentOnAddCallback = callback;
|
|
4248
|
+
callback(value, key);
|
|
4249
|
+
onAddCalls.delete(callback);
|
|
4250
|
+
currentOnAddCallback = undefined;
|
|
4251
|
+
});
|
|
4252
|
+
};
|
|
4253
|
+
const onRemove = function (ref, callback) {
|
|
4254
|
+
return $root.addCallback($root.refIds.get(ref), OPERATION.DELETE, callback);
|
|
4255
|
+
};
|
|
4256
|
+
return new Proxy({
|
|
4257
|
+
onAdd: function (callback, immediate = true) {
|
|
4258
|
+
//
|
|
4259
|
+
// https://github.com/colyseus/schema/issues/147
|
|
4260
|
+
// If parent instance has "onAdd" registered, avoid triggering immediate callback.
|
|
4261
|
+
//
|
|
4262
|
+
if (context.instance) {
|
|
4263
|
+
return onAdd(context.instance, callback, immediate && !onAddCalls.has(currentOnAddCallback));
|
|
4264
|
+
}
|
|
4265
|
+
else if (context.onInstanceAvailable) {
|
|
4266
|
+
// collection instance not received yet
|
|
4267
|
+
let detachCallback = () => { };
|
|
4268
|
+
context.onInstanceAvailable((ref, existing) => {
|
|
4269
|
+
detachCallback = onAdd(ref, callback, immediate && existing && !onAddCalls.has(currentOnAddCallback));
|
|
4270
|
+
});
|
|
4271
|
+
return () => detachCallback();
|
|
4272
|
+
}
|
|
4273
|
+
},
|
|
4274
|
+
onRemove: function (callback) {
|
|
4275
|
+
if (context.onInstanceAvailable) {
|
|
4276
|
+
// collection instance not received yet
|
|
4277
|
+
let detachCallback = () => { };
|
|
4278
|
+
context.onInstanceAvailable((ref) => {
|
|
4279
|
+
detachCallback = onRemove(ref, callback);
|
|
4280
|
+
});
|
|
4281
|
+
return () => detachCallback();
|
|
4282
|
+
}
|
|
4283
|
+
else if (context.instance) {
|
|
4284
|
+
return onRemove(context.instance, callback);
|
|
4285
|
+
}
|
|
4286
|
+
},
|
|
4287
|
+
}, {
|
|
4288
|
+
get(target, prop) {
|
|
4289
|
+
if (!target[prop]) {
|
|
4290
|
+
throw new Error(`Can't access '${prop}' through callback proxy. access the instance directly.`);
|
|
4291
|
+
}
|
|
4292
|
+
return target[prop];
|
|
4293
|
+
},
|
|
4294
|
+
has(target, prop) { return target[prop] !== undefined; },
|
|
4295
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4296
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4297
|
+
});
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
function $(instance) {
|
|
4301
|
+
return getProxy(undefined, { instance });
|
|
4302
|
+
}
|
|
4303
|
+
return $;
|
|
4304
|
+
}
|
|
4305
|
+
|
|
4306
|
+
function getRawChangesCallback(decoder, callback) {
|
|
4307
|
+
decoder.triggerChanges = callback;
|
|
4308
|
+
}
|
|
4309
|
+
|
|
3939
4310
|
class StateView {
|
|
3940
4311
|
constructor() {
|
|
3941
4312
|
/**
|
|
@@ -3953,20 +4324,21 @@ class StateView {
|
|
|
3953
4324
|
this.changes = new Map();
|
|
3954
4325
|
}
|
|
3955
4326
|
// TODO: allow to set multiple tags at once
|
|
3956
|
-
add(obj, tag = DEFAULT_VIEW_TAG) {
|
|
4327
|
+
add(obj, tag = DEFAULT_VIEW_TAG, checkIncludeParent = true) {
|
|
3957
4328
|
if (!obj[$changes]) {
|
|
3958
4329
|
console.warn("StateView#add(), invalid object:", obj);
|
|
3959
4330
|
return this;
|
|
3960
4331
|
}
|
|
3961
|
-
let changeTree = obj[$changes];
|
|
3962
|
-
this.items.add(changeTree);
|
|
3963
|
-
// Add children of this ChangeTree to this view
|
|
3964
|
-
changeTree.forEachChild((change, _) => this.add(change.ref, tag));
|
|
3965
4332
|
// FIXME: ArraySchema/MapSchema does not have metadata
|
|
3966
4333
|
const metadata = obj.constructor[Symbol.metadata];
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
4334
|
+
const changeTree = obj[$changes];
|
|
4335
|
+
this.items.add(changeTree);
|
|
4336
|
+
// add parent ChangeTree's
|
|
4337
|
+
// - if it was invisible to this view
|
|
4338
|
+
// - if it were previously filtered out
|
|
4339
|
+
if (checkIncludeParent && changeTree.parent) {
|
|
4340
|
+
this.addParent(changeTree.parent[$changes], changeTree.parentIndex, tag);
|
|
4341
|
+
}
|
|
3970
4342
|
//
|
|
3971
4343
|
// TODO: when adding an item of a MapSchema, the changes may not
|
|
3972
4344
|
// be set (only the parent's changes are set)
|
|
@@ -3990,7 +4362,6 @@ class StateView {
|
|
|
3990
4362
|
tags = this.tags.get(changeTree);
|
|
3991
4363
|
}
|
|
3992
4364
|
tags.add(tag);
|
|
3993
|
-
// console.log("BY TAG:", tag);
|
|
3994
4365
|
// Ref: add tagged properties
|
|
3995
4366
|
metadata?.[-3]?.[tag]?.forEach((index) => {
|
|
3996
4367
|
if (changeTree.getChange(index) !== OPERATION.DELETE) {
|
|
@@ -3999,73 +4370,63 @@ class StateView {
|
|
|
3999
4370
|
});
|
|
4000
4371
|
}
|
|
4001
4372
|
else {
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
// metadata?.[-3]?.[DEFAULT_VIEW_TAG]?.forEach((index) => {
|
|
4005
|
-
// if (changeTree.getChange(index) !== OPERATION.DELETE) {
|
|
4006
|
-
// changes.set(index, OPERATION.ADD);
|
|
4007
|
-
// }
|
|
4008
|
-
// });
|
|
4009
|
-
const allChangesSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
|
|
4373
|
+
const isInvisible = this.invisible.has(changeTree);
|
|
4374
|
+
const changeSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
|
|
4010
4375
|
? changeTree.allFilteredChanges
|
|
4011
4376
|
: changeTree.allChanges;
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4377
|
+
changeSet.forEach((op, index) => {
|
|
4378
|
+
const tagAtIndex = metadata?.[metadata?.[index]].tag;
|
|
4379
|
+
if ((isInvisible || // if "invisible", include all
|
|
4380
|
+
tagAtIndex === undefined || // "all change" with no tag
|
|
4381
|
+
tagAtIndex === tag // tagged property
|
|
4382
|
+
) &&
|
|
4383
|
+
op !== OPERATION.DELETE) {
|
|
4384
|
+
changes.set(index, op);
|
|
4018
4385
|
}
|
|
4019
|
-
}
|
|
4020
|
-
}
|
|
4021
|
-
// TODO: avoid unnecessary iteration here
|
|
4022
|
-
while (changeTree.parent &&
|
|
4023
|
-
(changeTree = changeTree.parent[$changes]) &&
|
|
4024
|
-
(changeTree.isFiltered || changeTree.isPartiallyFiltered)) {
|
|
4025
|
-
this.items.add(changeTree);
|
|
4386
|
+
});
|
|
4026
4387
|
}
|
|
4388
|
+
// Add children of this ChangeTree to this view
|
|
4389
|
+
changeTree.forEachChild((change, index) => {
|
|
4390
|
+
// Do not ADD children that don't have the same tag
|
|
4391
|
+
if (metadata && metadata[metadata[index]].tag !== tag) {
|
|
4392
|
+
return;
|
|
4393
|
+
}
|
|
4394
|
+
this.add(change.ref, tag, false);
|
|
4395
|
+
});
|
|
4027
4396
|
return this;
|
|
4028
4397
|
}
|
|
4029
|
-
addParent(changeTree, tag) {
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4398
|
+
addParent(changeTree, parentIndex, tag) {
|
|
4399
|
+
// view must have all "changeTree" parent tree
|
|
4400
|
+
this.items.add(changeTree);
|
|
4401
|
+
// add parent's parent
|
|
4402
|
+
const parentChangeTree = changeTree.parent?.[$changes];
|
|
4403
|
+
if (parentChangeTree && (parentChangeTree.isFiltered || parentChangeTree.isPartiallyFiltered)) {
|
|
4404
|
+
this.addParent(parentChangeTree, changeTree.parentIndex, tag);
|
|
4033
4405
|
}
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
if (!this.invisible.has(parentChangeTree)) {
|
|
4037
|
-
// parent is already available, no need to add it!
|
|
4406
|
+
// parent is already available, no need to add it!
|
|
4407
|
+
if (!this.invisible.has(changeTree)) {
|
|
4038
4408
|
return;
|
|
4039
4409
|
}
|
|
4040
|
-
this.addParent(parentChangeTree, tag);
|
|
4041
4410
|
// add parent's tag properties
|
|
4042
|
-
if (
|
|
4043
|
-
let
|
|
4044
|
-
if (
|
|
4045
|
-
|
|
4046
|
-
this.changes.set(
|
|
4411
|
+
if (changeTree.getChange(parentIndex) !== OPERATION.DELETE) {
|
|
4412
|
+
let changes = this.changes.get(changeTree);
|
|
4413
|
+
if (changes === undefined) {
|
|
4414
|
+
changes = new Map();
|
|
4415
|
+
this.changes.set(changeTree, changes);
|
|
4047
4416
|
}
|
|
4048
|
-
// console.log("add parent change", {
|
|
4049
|
-
// parentIndex,
|
|
4050
|
-
// parentChanges,
|
|
4051
|
-
// parentChange: (
|
|
4052
|
-
// parentChangeTree.getChange(parentIndex) &&
|
|
4053
|
-
// OPERATION[parentChangeTree.getChange(parentIndex)]
|
|
4054
|
-
// ),
|
|
4055
|
-
// })
|
|
4056
4417
|
if (!this.tags) {
|
|
4057
4418
|
this.tags = new WeakMap();
|
|
4058
4419
|
}
|
|
4059
4420
|
let tags;
|
|
4060
|
-
if (!this.tags.has(
|
|
4421
|
+
if (!this.tags.has(changeTree)) {
|
|
4061
4422
|
tags = new Set();
|
|
4062
|
-
this.tags.set(
|
|
4423
|
+
this.tags.set(changeTree, tags);
|
|
4063
4424
|
}
|
|
4064
4425
|
else {
|
|
4065
|
-
tags = this.tags.get(
|
|
4426
|
+
tags = this.tags.get(changeTree);
|
|
4066
4427
|
}
|
|
4067
4428
|
tags.add(tag);
|
|
4068
|
-
|
|
4429
|
+
changes.set(parentIndex, OPERATION.ADD);
|
|
4069
4430
|
}
|
|
4070
4431
|
}
|
|
4071
4432
|
remove(obj, tag = DEFAULT_VIEW_TAG) {
|
|
@@ -4129,5 +4490,5 @@ registerType("array", { constructor: ArraySchema });
|
|
|
4129
4490
|
registerType("set", { constructor: SetSchema });
|
|
4130
4491
|
registerType("collection", { constructor: CollectionSchema, });
|
|
4131
4492
|
|
|
4132
|
-
export { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $track, ArraySchema, ChangeTree, CollectionSchema, Decoder, Encoder, MapSchema, Metadata, OPERATION, Reflection, ReflectionField, ReflectionType, Schema, SetSchema, StateView, TypeContext, decode, decodeKeyValueOperation, decodeSchemaOperation, defineTypes, deprecated, dumpChanges, encode, encodeArray as encodeKeyValueOperation, encodeSchemaOperation, registerType, type, view };
|
|
4493
|
+
export { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $track, ArraySchema, ChangeTree, CollectionSchema, Decoder, Encoder, MapSchema, Metadata, OPERATION, Reflection, ReflectionField, ReflectionType, Schema, SetSchema, StateView, TypeContext, decode, decodeKeyValueOperation, decodeSchemaOperation, defineTypes, deprecated, dumpChanges, encode, encodeArray as encodeKeyValueOperation, encodeSchemaOperation, getDecoderStateCallbacks, getRawChangesCallback, registerType, type, view };
|
|
4133
4494
|
//# sourceMappingURL=index.mjs.map
|