@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/umd/index.js
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
OPERATION[OPERATION["REVERSE"] = 15] = "REVERSE";
|
|
30
30
|
OPERATION[OPERATION["MOVE"] = 32] = "MOVE";
|
|
31
31
|
OPERATION[OPERATION["DELETE_BY_REFID"] = 33] = "DELETE_BY_REFID";
|
|
32
|
+
OPERATION[OPERATION["ADD_BY_REFID"] = 129] = "ADD_BY_REFID";
|
|
32
33
|
})(exports.OPERATION || (exports.OPERATION = {}));
|
|
33
34
|
|
|
34
35
|
Symbol.metadata ??= Symbol.for("Symbol.metadata");
|
|
@@ -145,6 +146,45 @@
|
|
|
145
146
|
isDeprecated(metadata, field) {
|
|
146
147
|
return metadata[field].deprecated === true;
|
|
147
148
|
},
|
|
149
|
+
init(klass) {
|
|
150
|
+
//
|
|
151
|
+
// Used only to initialize an empty Schema (Encoder#constructor)
|
|
152
|
+
// TODO: remove/refactor this...
|
|
153
|
+
//
|
|
154
|
+
const metadata = {};
|
|
155
|
+
klass[Symbol.metadata] = metadata;
|
|
156
|
+
Object.defineProperty(metadata, -1, {
|
|
157
|
+
value: 0,
|
|
158
|
+
enumerable: false,
|
|
159
|
+
configurable: true,
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
initialize(constructor, parentMetadata) {
|
|
163
|
+
let metadata = constructor[Symbol.metadata] ?? Object.create(null);
|
|
164
|
+
// make sure inherited classes have their own metadata object.
|
|
165
|
+
if (constructor[Symbol.metadata] === parentMetadata) {
|
|
166
|
+
metadata = Object.create(null);
|
|
167
|
+
if (parentMetadata) {
|
|
168
|
+
// assign parent metadata to current
|
|
169
|
+
Object.assign(metadata, parentMetadata);
|
|
170
|
+
for (let i = 0; i <= parentMetadata[-1]; i++) {
|
|
171
|
+
Object.defineProperty(metadata, i, {
|
|
172
|
+
value: parentMetadata[i],
|
|
173
|
+
enumerable: false,
|
|
174
|
+
configurable: true,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
Object.defineProperty(metadata, -1, {
|
|
178
|
+
value: parentMetadata[-1],
|
|
179
|
+
enumerable: false,
|
|
180
|
+
configurable: true,
|
|
181
|
+
writable: true,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
constructor[Symbol.metadata] = metadata;
|
|
186
|
+
return metadata;
|
|
187
|
+
},
|
|
148
188
|
isValidInstance(klass) {
|
|
149
189
|
return (klass.constructor[Symbol.metadata] &&
|
|
150
190
|
Object.prototype.hasOwnProperty.call(klass.constructor[Symbol.metadata], -1));
|
|
@@ -160,44 +200,6 @@
|
|
|
160
200
|
};
|
|
161
201
|
|
|
162
202
|
var _a$5;
|
|
163
|
-
class Root {
|
|
164
|
-
constructor() {
|
|
165
|
-
this.nextUniqueId = 0;
|
|
166
|
-
this.refCount = new WeakMap();
|
|
167
|
-
// all changes
|
|
168
|
-
this.allChanges = new Map();
|
|
169
|
-
this.allFilteredChanges = new Map();
|
|
170
|
-
// pending changes to be encoded
|
|
171
|
-
this.changes = new Map();
|
|
172
|
-
this.filteredChanges = new Map();
|
|
173
|
-
}
|
|
174
|
-
getNextUniqueId() {
|
|
175
|
-
return this.nextUniqueId++;
|
|
176
|
-
}
|
|
177
|
-
add(changeTree) {
|
|
178
|
-
const refCount = this.refCount.get(changeTree) || 0;
|
|
179
|
-
this.refCount.set(changeTree, refCount + 1);
|
|
180
|
-
}
|
|
181
|
-
remove(changeTree) {
|
|
182
|
-
const refCount = this.refCount.get(changeTree);
|
|
183
|
-
if (refCount <= 1) {
|
|
184
|
-
this.allChanges.delete(changeTree);
|
|
185
|
-
this.changes.delete(changeTree);
|
|
186
|
-
if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
|
|
187
|
-
this.allFilteredChanges.delete(changeTree);
|
|
188
|
-
this.filteredChanges.delete(changeTree);
|
|
189
|
-
}
|
|
190
|
-
this.refCount.delete(changeTree);
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
this.refCount.set(changeTree, refCount - 1);
|
|
194
|
-
}
|
|
195
|
-
changeTree.forEachChild((child, _) => this.remove(child));
|
|
196
|
-
}
|
|
197
|
-
clear() {
|
|
198
|
-
this.changes.clear();
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
203
|
class ChangeTree {
|
|
202
204
|
static { _a$5 = $isNew; }
|
|
203
205
|
;
|
|
@@ -229,8 +231,6 @@
|
|
|
229
231
|
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
230
232
|
this.root.allFilteredChanges.set(this, this.allFilteredChanges);
|
|
231
233
|
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
232
|
-
// } else {
|
|
233
|
-
// this.root.allChanges.set(this, this.allChanges);
|
|
234
234
|
}
|
|
235
235
|
if (!this.isFiltered) {
|
|
236
236
|
this.root.allChanges.set(this, this.allChanges);
|
|
@@ -264,14 +264,12 @@
|
|
|
264
264
|
this.checkIsFiltered(parent, parentIndex);
|
|
265
265
|
if (!this.isFiltered) {
|
|
266
266
|
this.root.changes.set(this, this.changes);
|
|
267
|
+
this.root.allChanges.set(this, this.allChanges);
|
|
267
268
|
}
|
|
268
269
|
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
269
270
|
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
270
271
|
this.root.allFilteredChanges.set(this, this.filteredChanges);
|
|
271
272
|
}
|
|
272
|
-
else {
|
|
273
|
-
this.root.allChanges.set(this, this.allChanges);
|
|
274
|
-
}
|
|
275
273
|
this.ensureRefId();
|
|
276
274
|
this.forEachChild((changeTree, atIndex) => {
|
|
277
275
|
changeTree.setParent(this.ref, root, atIndex);
|
|
@@ -295,7 +293,7 @@
|
|
|
295
293
|
// MapSchema / ArraySchema, etc.
|
|
296
294
|
this.ref.forEach((value, key) => {
|
|
297
295
|
if (Metadata.isValidInstance(value)) {
|
|
298
|
-
callback(value[$changes], this.ref[$changes].indexes[key]);
|
|
296
|
+
callback(value[$changes], this.ref[$changes].indexes[key] ?? key);
|
|
299
297
|
}
|
|
300
298
|
});
|
|
301
299
|
}
|
|
@@ -323,8 +321,9 @@
|
|
|
323
321
|
// TODO: are DELETE operations being encoded as ADD here ??
|
|
324
322
|
//
|
|
325
323
|
if (isFiltered) {
|
|
326
|
-
this.allFilteredChanges.set(index, exports.OPERATION.ADD);
|
|
327
324
|
this.root?.filteredChanges.set(this, this.filteredChanges);
|
|
325
|
+
this.allFilteredChanges.set(index, exports.OPERATION.ADD);
|
|
326
|
+
this.root?.allFilteredChanges.set(this, this.allFilteredChanges);
|
|
328
327
|
}
|
|
329
328
|
else {
|
|
330
329
|
this.allChanges.set(index, exports.OPERATION.ADD);
|
|
@@ -363,7 +362,6 @@
|
|
|
363
362
|
}
|
|
364
363
|
_shiftAllChangeIndexes(shiftIndex, startIndex = 0, allChangeSet) {
|
|
365
364
|
Array.from(allChangeSet.entries()).forEach(([index, op]) => {
|
|
366
|
-
// console.log('shiftAllChangeIndexes', index >= startIndex, { index, op, shiftIndex, startIndex })
|
|
367
365
|
if (index >= startIndex) {
|
|
368
366
|
allChangeSet.delete(index);
|
|
369
367
|
allChangeSet.set(index + shiftIndex, op);
|
|
@@ -504,16 +502,31 @@
|
|
|
504
502
|
}
|
|
505
503
|
checkIsFiltered(parent, parentIndex) {
|
|
506
504
|
// Detect if current structure has "filters" declared
|
|
507
|
-
this.isPartiallyFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-2];
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
505
|
+
this.isPartiallyFiltered = (this.ref['constructor']?.[Symbol.metadata]?.[-2] !== undefined);
|
|
506
|
+
if (parent && !Metadata.isValidInstance(parent)) {
|
|
507
|
+
const parentChangeTree = parent[$changes];
|
|
508
|
+
parent = parentChangeTree.parent;
|
|
509
|
+
parentIndex = parentChangeTree.parentIndex;
|
|
510
|
+
}
|
|
511
|
+
const parentMetadata = parent?.['constructor']?.[Symbol.metadata];
|
|
512
|
+
this.isFiltered = (parent &&
|
|
513
|
+
parentMetadata?.[-2]?.includes(parentIndex));
|
|
514
|
+
// this.isFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-4];
|
|
515
|
+
// // Detect if parent has "filters" declared
|
|
516
|
+
// while (parent && !this.isFiltered) {
|
|
517
|
+
// const metadata: Metadata = parent['constructor'][Symbol.metadata];
|
|
518
|
+
// // this.isFiltered = metadata?.[-4];
|
|
519
|
+
// const fieldName = metadata?.[parentIndex];
|
|
520
|
+
// const isParentOwned = metadata?.[fieldName]?.tag !== undefined;
|
|
521
|
+
// this.isFiltered = isParentOwned || parent[$changes].isFiltered; // metadata?.[-2]
|
|
522
|
+
// parent = parent[$changes].parent;
|
|
523
|
+
// };
|
|
524
|
+
// console.log("ChangeTree.checkIsFiltered", {
|
|
525
|
+
// parent: parent?.constructor.name,
|
|
526
|
+
// ref: this.ref.constructor.name,
|
|
527
|
+
// isFiltered: this.isFiltered,
|
|
528
|
+
// isPartiallyFiltered: this.isPartiallyFiltered,
|
|
529
|
+
// });
|
|
517
530
|
//
|
|
518
531
|
// TODO: refactor this!
|
|
519
532
|
//
|
|
@@ -565,26 +578,29 @@
|
|
|
565
578
|
textEncoder = new TextEncoder();
|
|
566
579
|
}
|
|
567
580
|
catch (e) { }
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
581
|
+
const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength);
|
|
582
|
+
const utf8Length = (hasBufferByteLength)
|
|
583
|
+
? Buffer.byteLength // node
|
|
584
|
+
: function (str, _) {
|
|
585
|
+
var c = 0, length = 0;
|
|
586
|
+
for (var i = 0, l = str.length; i < l; i++) {
|
|
587
|
+
c = str.charCodeAt(i);
|
|
588
|
+
if (c < 0x80) {
|
|
589
|
+
length += 1;
|
|
590
|
+
}
|
|
591
|
+
else if (c < 0x800) {
|
|
592
|
+
length += 2;
|
|
593
|
+
}
|
|
594
|
+
else if (c < 0xd800 || c >= 0xe000) {
|
|
595
|
+
length += 3;
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
i++;
|
|
599
|
+
length += 4;
|
|
600
|
+
}
|
|
584
601
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
}
|
|
602
|
+
return length;
|
|
603
|
+
};
|
|
588
604
|
function utf8Write(view, str, it) {
|
|
589
605
|
var c = 0;
|
|
590
606
|
for (var i = 0, l = str.length; i < l; i++) {
|
|
@@ -679,8 +695,7 @@
|
|
|
679
695
|
if (!value) {
|
|
680
696
|
value = "";
|
|
681
697
|
}
|
|
682
|
-
|
|
683
|
-
let length = Buffer.byteLength(value, "utf8");
|
|
698
|
+
let length = utf8Length(value, "utf8");
|
|
684
699
|
let size = 0;
|
|
685
700
|
// fixstr
|
|
686
701
|
if (length < 0x20) {
|
|
@@ -791,23 +806,23 @@
|
|
|
791
806
|
|
|
792
807
|
var encode = /*#__PURE__*/Object.freeze({
|
|
793
808
|
__proto__: null,
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
uint8: uint8$1,
|
|
809
|
+
boolean: boolean$1,
|
|
810
|
+
float32: float32$1,
|
|
811
|
+
float64: float64$1,
|
|
798
812
|
int16: int16$1,
|
|
799
|
-
uint16: uint16$1,
|
|
800
813
|
int32: int32$1,
|
|
801
|
-
uint32: uint32$1,
|
|
802
814
|
int64: int64$1,
|
|
815
|
+
int8: int8$1,
|
|
816
|
+
number: number$1,
|
|
817
|
+
string: string$1,
|
|
818
|
+
uint16: uint16$1,
|
|
819
|
+
uint32: uint32$1,
|
|
803
820
|
uint64: uint64$1,
|
|
804
|
-
|
|
805
|
-
|
|
821
|
+
uint8: uint8$1,
|
|
822
|
+
utf8Length: utf8Length,
|
|
823
|
+
utf8Write: utf8Write,
|
|
806
824
|
writeFloat32: writeFloat32,
|
|
807
|
-
writeFloat64: writeFloat64
|
|
808
|
-
boolean: boolean$1,
|
|
809
|
-
string: string$1,
|
|
810
|
-
number: number$1
|
|
825
|
+
writeFloat64: writeFloat64
|
|
811
826
|
});
|
|
812
827
|
|
|
813
828
|
class EncodeSchemaError extends Error {
|
|
@@ -949,6 +964,18 @@
|
|
|
949
964
|
}
|
|
950
965
|
const type = changeTree.getType(field);
|
|
951
966
|
const value = changeTree.getValue(field);
|
|
967
|
+
// try { throw new Error(); } catch (e) {
|
|
968
|
+
// // only print if not coming from Reflection.ts
|
|
969
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
970
|
+
// console.log("encodeKeyValueOperation -> ", {
|
|
971
|
+
// ref: changeTree.ref.constructor.name,
|
|
972
|
+
// field,
|
|
973
|
+
// operation: OPERATION[operation],
|
|
974
|
+
// value: value?.toJSON(),
|
|
975
|
+
// items: ref.toJSON(),
|
|
976
|
+
// });
|
|
977
|
+
// }
|
|
978
|
+
// }
|
|
952
979
|
// TODO: inline this function call small performance gain
|
|
953
980
|
encodeValue(encoder, bytes, ref, type, value, field, operation, it);
|
|
954
981
|
};
|
|
@@ -958,15 +985,19 @@
|
|
|
958
985
|
*/
|
|
959
986
|
const encodeArray = function (encoder, bytes, changeTree, field, operation, it, isEncodeAll, hasView) {
|
|
960
987
|
const ref = changeTree.ref;
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
988
|
+
const useOperationByRefId = hasView && changeTree.isFiltered && (typeof (changeTree.getType(field)) !== "string");
|
|
989
|
+
let refOrIndex;
|
|
990
|
+
if (useOperationByRefId) {
|
|
991
|
+
refOrIndex = ref['tmpItems'][field][$changes].refId;
|
|
992
|
+
if (operation === exports.OPERATION.DELETE) {
|
|
993
|
+
operation = exports.OPERATION.DELETE_BY_REFID;
|
|
994
|
+
}
|
|
995
|
+
else if (operation === exports.OPERATION.ADD) {
|
|
996
|
+
operation = exports.OPERATION.ADD_BY_REFID;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
else {
|
|
1000
|
+
refOrIndex = field;
|
|
970
1001
|
}
|
|
971
1002
|
// encode operation
|
|
972
1003
|
bytes[it.offset++] = operation & 255;
|
|
@@ -975,7 +1006,7 @@
|
|
|
975
1006
|
return;
|
|
976
1007
|
}
|
|
977
1008
|
// encode index
|
|
978
|
-
number$1(bytes,
|
|
1009
|
+
number$1(bytes, refOrIndex, it);
|
|
979
1010
|
// Do not encode value for DELETE operations
|
|
980
1011
|
if (operation === exports.OPERATION.DELETE) {
|
|
981
1012
|
return;
|
|
@@ -1016,7 +1047,6 @@
|
|
|
1016
1047
|
* SOFTWARE
|
|
1017
1048
|
*/
|
|
1018
1049
|
function utf8Read(bytes, it, length) {
|
|
1019
|
-
it.offset += length;
|
|
1020
1050
|
var string = '', chr = 0;
|
|
1021
1051
|
for (var i = it.offset, end = it.offset + length; i < end; i++) {
|
|
1022
1052
|
var byte = bytes[i];
|
|
@@ -1053,6 +1083,7 @@
|
|
|
1053
1083
|
// (do not throw error to avoid server/client from crashing due to hack attemps)
|
|
1054
1084
|
// throw new Error('Invalid byte ' + byte.toString(16));
|
|
1055
1085
|
}
|
|
1086
|
+
it.offset += length;
|
|
1056
1087
|
return string;
|
|
1057
1088
|
}
|
|
1058
1089
|
function int8(bytes, it) {
|
|
@@ -1224,31 +1255,31 @@
|
|
|
1224
1255
|
|
|
1225
1256
|
var decode = /*#__PURE__*/Object.freeze({
|
|
1226
1257
|
__proto__: null,
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
uint8: uint8,
|
|
1230
|
-
int16: int16,
|
|
1231
|
-
uint16: uint16,
|
|
1232
|
-
int32: int32,
|
|
1233
|
-
uint32: uint32,
|
|
1258
|
+
arrayCheck: arrayCheck,
|
|
1259
|
+
boolean: boolean,
|
|
1234
1260
|
float32: float32,
|
|
1235
1261
|
float64: float64,
|
|
1262
|
+
int16: int16,
|
|
1263
|
+
int32: int32,
|
|
1236
1264
|
int64: int64,
|
|
1237
|
-
|
|
1265
|
+
int8: int8,
|
|
1266
|
+
number: number,
|
|
1267
|
+
numberCheck: numberCheck,
|
|
1238
1268
|
readFloat32: readFloat32,
|
|
1239
1269
|
readFloat64: readFloat64,
|
|
1240
|
-
boolean: boolean,
|
|
1241
1270
|
string: string,
|
|
1242
1271
|
stringCheck: stringCheck,
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1272
|
+
switchStructureCheck: switchStructureCheck,
|
|
1273
|
+
uint16: uint16,
|
|
1274
|
+
uint32: uint32,
|
|
1275
|
+
uint64: uint64,
|
|
1276
|
+
uint8: uint8,
|
|
1277
|
+
utf8Read: utf8Read
|
|
1247
1278
|
});
|
|
1248
1279
|
|
|
1249
1280
|
const DEFINITION_MISMATCH = -1;
|
|
1250
1281
|
function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges) {
|
|
1251
|
-
const $root = decoder
|
|
1282
|
+
const $root = decoder.root;
|
|
1252
1283
|
const previousValue = ref[$getByIndex](index);
|
|
1253
1284
|
let value;
|
|
1254
1285
|
if ((operation & exports.OPERATION.DELETE) === exports.OPERATION.DELETE) {
|
|
@@ -1354,6 +1385,7 @@
|
|
|
1354
1385
|
// skip early if field is not defined
|
|
1355
1386
|
const field = metadata[index];
|
|
1356
1387
|
if (field === undefined) {
|
|
1388
|
+
console.warn("@colyseus/schema: field not defined at", { index, ref: ref.constructor.name, metadata });
|
|
1357
1389
|
return DEFINITION_MISMATCH;
|
|
1358
1390
|
}
|
|
1359
1391
|
const { value, previousValue } = decodeValue(decoder, operation, ref, index, metadata[field].type, bytes, it, allChanges);
|
|
@@ -1434,7 +1466,8 @@
|
|
|
1434
1466
|
};
|
|
1435
1467
|
const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
1436
1468
|
// "uncompressed" index + operation (array/map items)
|
|
1437
|
-
|
|
1469
|
+
let operation = bytes[it.offset++];
|
|
1470
|
+
let index;
|
|
1438
1471
|
if (operation === exports.OPERATION.CLEAR) {
|
|
1439
1472
|
//
|
|
1440
1473
|
// When decoding:
|
|
@@ -1448,8 +1481,8 @@
|
|
|
1448
1481
|
else if (operation === exports.OPERATION.DELETE_BY_REFID) {
|
|
1449
1482
|
// TODO: refactor here, try to follow same flow as below
|
|
1450
1483
|
const refId = number(bytes, it);
|
|
1451
|
-
const previousValue = decoder
|
|
1452
|
-
|
|
1484
|
+
const previousValue = decoder.root.refs.get(refId);
|
|
1485
|
+
index = ref.findIndex((value) => value === previousValue);
|
|
1453
1486
|
ref[$deleteByIndex](index);
|
|
1454
1487
|
allChanges.push({
|
|
1455
1488
|
ref,
|
|
@@ -1462,7 +1495,18 @@
|
|
|
1462
1495
|
});
|
|
1463
1496
|
return;
|
|
1464
1497
|
}
|
|
1465
|
-
|
|
1498
|
+
else if (operation === exports.OPERATION.ADD_BY_REFID) {
|
|
1499
|
+
// operation = OPERATION.ADD;
|
|
1500
|
+
const refId = number(bytes, it);
|
|
1501
|
+
const itemByRefId = decoder.root.refs.get(refId);
|
|
1502
|
+
// use existing index, or push new value
|
|
1503
|
+
index = (itemByRefId)
|
|
1504
|
+
? ref.findIndex((value) => value === itemByRefId)
|
|
1505
|
+
: ref.length;
|
|
1506
|
+
}
|
|
1507
|
+
else {
|
|
1508
|
+
index = number(bytes, it);
|
|
1509
|
+
}
|
|
1466
1510
|
const type = ref[$childType];
|
|
1467
1511
|
let dynamicIndex = index;
|
|
1468
1512
|
const { value, previousValue } = decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges);
|
|
@@ -1623,7 +1667,6 @@
|
|
|
1623
1667
|
}
|
|
1624
1668
|
const changeTree = this[$changes];
|
|
1625
1669
|
changeTree.indexedOperation(length, exports.OPERATION.ADD, this.items.length);
|
|
1626
|
-
// changeTree.indexes[length] = length;
|
|
1627
1670
|
this.items.push(value);
|
|
1628
1671
|
this.tmpItems.push(value);
|
|
1629
1672
|
//
|
|
@@ -1871,14 +1914,6 @@
|
|
|
1871
1914
|
lastIndexOf(searchElement, fromIndex = this.length - 1) {
|
|
1872
1915
|
return this.items.lastIndexOf(searchElement, fromIndex);
|
|
1873
1916
|
}
|
|
1874
|
-
/**
|
|
1875
|
-
* Determines whether all the members of an array satisfy the specified test.
|
|
1876
|
-
* @param callbackfn A function that accepts up to three arguments. The every method calls
|
|
1877
|
-
* the callbackfn function for each element in the array until the callbackfn returns a value
|
|
1878
|
-
* which is coercible to the Boolean value false, or until the end of the array.
|
|
1879
|
-
* @param thisArg An object to which the this keyword can refer in the callbackfn function.
|
|
1880
|
-
* If thisArg is omitted, undefined is used as the this value.
|
|
1881
|
-
*/
|
|
1882
1917
|
every(callbackfn, thisArg) {
|
|
1883
1918
|
return this.items.every(callbackfn, thisArg);
|
|
1884
1919
|
}
|
|
@@ -2322,7 +2357,6 @@
|
|
|
2322
2357
|
}
|
|
2323
2358
|
registerType("map", { constructor: MapSchema });
|
|
2324
2359
|
|
|
2325
|
-
const DEFAULT_VIEW_TAG = -1;
|
|
2326
2360
|
class TypeContext {
|
|
2327
2361
|
/**
|
|
2328
2362
|
* For inheritance support
|
|
@@ -2344,6 +2378,7 @@
|
|
|
2344
2378
|
this.types = {};
|
|
2345
2379
|
this.schemas = new Map();
|
|
2346
2380
|
this.hasFilters = false;
|
|
2381
|
+
this.parentFiltered = {};
|
|
2347
2382
|
if (rootClass) {
|
|
2348
2383
|
this.discoverTypes(rootClass);
|
|
2349
2384
|
}
|
|
@@ -2360,32 +2395,46 @@
|
|
|
2360
2395
|
return false;
|
|
2361
2396
|
}
|
|
2362
2397
|
this.types[typeid] = schema;
|
|
2398
|
+
//
|
|
2399
|
+
// Workaround to allow using an empty Schema (with no `@type()` fields)
|
|
2400
|
+
//
|
|
2401
|
+
if (schema[Symbol.metadata] === undefined) {
|
|
2402
|
+
Metadata.init(schema);
|
|
2403
|
+
}
|
|
2363
2404
|
this.schemas.set(schema, typeid);
|
|
2364
2405
|
return true;
|
|
2365
2406
|
}
|
|
2366
2407
|
getTypeId(klass) {
|
|
2367
2408
|
return this.schemas.get(klass);
|
|
2368
2409
|
}
|
|
2369
|
-
discoverTypes(klass) {
|
|
2410
|
+
discoverTypes(klass, parentIndex, parentFieldViewTag) {
|
|
2370
2411
|
if (!this.add(klass)) {
|
|
2371
2412
|
return;
|
|
2372
2413
|
}
|
|
2373
2414
|
// add classes inherited from this base class
|
|
2374
2415
|
TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
|
|
2375
|
-
this.discoverTypes(child);
|
|
2416
|
+
this.discoverTypes(child, parentIndex, parentFieldViewTag);
|
|
2376
2417
|
});
|
|
2377
|
-
|
|
2378
|
-
if (klass[Symbol.metadata] === undefined) {
|
|
2379
|
-
klass[Symbol.metadata] = {};
|
|
2380
|
-
}
|
|
2381
|
-
// const metadata = Metadata.getFor(klass);
|
|
2382
|
-
const metadata = klass[Symbol.metadata];
|
|
2418
|
+
const metadata = (klass[Symbol.metadata] ??= {});
|
|
2383
2419
|
// if any schema/field has filters, mark "context" as having filters.
|
|
2384
2420
|
if (metadata[-2]) {
|
|
2385
2421
|
this.hasFilters = true;
|
|
2386
2422
|
}
|
|
2423
|
+
if (parentFieldViewTag !== undefined) {
|
|
2424
|
+
this.parentFiltered[`${this.schemas.get(klass)}-${parentIndex}`] = true;
|
|
2425
|
+
}
|
|
2387
2426
|
for (const field in metadata) {
|
|
2427
|
+
// //
|
|
2428
|
+
// // Modify the field's metadata to include the parent field's view tag
|
|
2429
|
+
// //
|
|
2430
|
+
// if (
|
|
2431
|
+
// parentFieldViewTag !== undefined &&
|
|
2432
|
+
// metadata[field].tag === undefined
|
|
2433
|
+
// ) {
|
|
2434
|
+
// metadata[field].tag = parentFieldViewTag;
|
|
2435
|
+
// }
|
|
2388
2436
|
const fieldType = metadata[field].type;
|
|
2437
|
+
const viewTag = metadata[field].tag;
|
|
2389
2438
|
if (typeof (fieldType) === "string") {
|
|
2390
2439
|
continue;
|
|
2391
2440
|
}
|
|
@@ -2394,10 +2443,10 @@
|
|
|
2394
2443
|
if (type === "string") {
|
|
2395
2444
|
continue;
|
|
2396
2445
|
}
|
|
2397
|
-
this.discoverTypes(type);
|
|
2446
|
+
this.discoverTypes(type, metadata[field].index, viewTag);
|
|
2398
2447
|
}
|
|
2399
2448
|
else if (typeof (fieldType) === "function") {
|
|
2400
|
-
this.discoverTypes(fieldType);
|
|
2449
|
+
this.discoverTypes(fieldType, viewTag);
|
|
2401
2450
|
}
|
|
2402
2451
|
else {
|
|
2403
2452
|
const type = Object.values(fieldType)[0];
|
|
@@ -2405,11 +2454,13 @@
|
|
|
2405
2454
|
if (typeof (type) === "string") {
|
|
2406
2455
|
continue;
|
|
2407
2456
|
}
|
|
2408
|
-
this.discoverTypes(type);
|
|
2457
|
+
this.discoverTypes(type, metadata[field].index, viewTag);
|
|
2409
2458
|
}
|
|
2410
2459
|
}
|
|
2411
2460
|
}
|
|
2412
2461
|
}
|
|
2462
|
+
|
|
2463
|
+
const DEFAULT_VIEW_TAG = -1;
|
|
2413
2464
|
/**
|
|
2414
2465
|
* [See documentation](https://docs.colyseus.io/state/schema/)
|
|
2415
2466
|
*
|
|
@@ -2557,6 +2608,7 @@
|
|
|
2557
2608
|
const constructor = target.constructor;
|
|
2558
2609
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
2559
2610
|
const parentMetadata = parentClass[Symbol.metadata];
|
|
2611
|
+
// TODO: use Metadata.initialize()
|
|
2560
2612
|
const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
|
|
2561
2613
|
if (!metadata[fieldName]) {
|
|
2562
2614
|
//
|
|
@@ -2581,8 +2633,8 @@
|
|
|
2581
2633
|
// for inheritance support
|
|
2582
2634
|
TypeContext.register(constructor);
|
|
2583
2635
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
2584
|
-
const parentMetadata = parentClass[Symbol.metadata];
|
|
2585
|
-
const metadata =
|
|
2636
|
+
const parentMetadata = parentClass && parentClass[Symbol.metadata];
|
|
2637
|
+
const metadata = Metadata.initialize(constructor, parentMetadata);
|
|
2586
2638
|
let fieldIndex;
|
|
2587
2639
|
/**
|
|
2588
2640
|
* skip if descriptor already exists for this field (`@deprecated()`)
|
|
@@ -2963,13 +3015,13 @@
|
|
|
2963
3015
|
}
|
|
2964
3016
|
return output;
|
|
2965
3017
|
}
|
|
2966
|
-
static debugChangesDeep(ref) {
|
|
3018
|
+
static debugChangesDeep(ref, changeSetName = "changes") {
|
|
2967
3019
|
let output = "";
|
|
2968
3020
|
const rootChangeTree = ref[$changes];
|
|
2969
3021
|
const changeTrees = new Map();
|
|
2970
3022
|
let totalInstances = 0;
|
|
2971
3023
|
let totalOperations = 0;
|
|
2972
|
-
for (const [changeTree, changes] of (rootChangeTree.root.
|
|
3024
|
+
for (const [changeTree, changes] of (rootChangeTree.root[changeSetName].entries())) {
|
|
2973
3025
|
let includeChangeTree = false;
|
|
2974
3026
|
let parentChangeTrees = [];
|
|
2975
3027
|
let parentChangeTree = changeTree.parent?.[$changes];
|
|
@@ -3355,6 +3407,8 @@
|
|
|
3355
3407
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
3356
3408
|
PERFORMANCE OF THIS SOFTWARE.
|
|
3357
3409
|
***************************************************************************** */
|
|
3410
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
3411
|
+
|
|
3358
3412
|
|
|
3359
3413
|
function __decorate(decorators, target, key, desc) {
|
|
3360
3414
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -3368,29 +3422,68 @@
|
|
|
3368
3422
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
3369
3423
|
};
|
|
3370
3424
|
|
|
3425
|
+
class Root {
|
|
3426
|
+
constructor(types) {
|
|
3427
|
+
this.types = types;
|
|
3428
|
+
this.nextUniqueId = 0;
|
|
3429
|
+
this.refCount = new WeakMap();
|
|
3430
|
+
// all changes
|
|
3431
|
+
this.allChanges = new Map();
|
|
3432
|
+
this.allFilteredChanges = new Map();
|
|
3433
|
+
// pending changes to be encoded
|
|
3434
|
+
this.changes = new Map();
|
|
3435
|
+
this.filteredChanges = new Map();
|
|
3436
|
+
}
|
|
3437
|
+
getNextUniqueId() {
|
|
3438
|
+
return this.nextUniqueId++;
|
|
3439
|
+
}
|
|
3440
|
+
add(changeTree) {
|
|
3441
|
+
const refCount = this.refCount.get(changeTree) || 0;
|
|
3442
|
+
this.refCount.set(changeTree, refCount + 1);
|
|
3443
|
+
}
|
|
3444
|
+
remove(changeTree) {
|
|
3445
|
+
const refCount = this.refCount.get(changeTree);
|
|
3446
|
+
if (refCount <= 1) {
|
|
3447
|
+
this.allChanges.delete(changeTree);
|
|
3448
|
+
this.changes.delete(changeTree);
|
|
3449
|
+
if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
|
|
3450
|
+
this.allFilteredChanges.delete(changeTree);
|
|
3451
|
+
this.filteredChanges.delete(changeTree);
|
|
3452
|
+
}
|
|
3453
|
+
this.refCount.delete(changeTree);
|
|
3454
|
+
}
|
|
3455
|
+
else {
|
|
3456
|
+
this.refCount.set(changeTree, refCount - 1);
|
|
3457
|
+
}
|
|
3458
|
+
changeTree.forEachChild((child, _) => this.remove(child));
|
|
3459
|
+
}
|
|
3460
|
+
clear() {
|
|
3461
|
+
this.changes.clear();
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
|
|
3371
3465
|
class Encoder {
|
|
3372
3466
|
static { this.BUFFER_SIZE = 8 * 1024; } // 8KB
|
|
3373
|
-
constructor(
|
|
3467
|
+
constructor(state) {
|
|
3374
3468
|
this.sharedBuffer = Buffer.allocUnsafeSlow(Encoder.BUFFER_SIZE);
|
|
3375
|
-
this.setRoot(root);
|
|
3376
3469
|
//
|
|
3377
3470
|
// TODO: cache and restore "Context" based on root schema
|
|
3378
3471
|
// (to avoid creating a new context for every new room)
|
|
3379
3472
|
//
|
|
3380
|
-
this.context = new TypeContext(
|
|
3473
|
+
this.context = new TypeContext(state.constructor);
|
|
3474
|
+
this.root = new Root(this.context);
|
|
3475
|
+
this.setState(state);
|
|
3381
3476
|
// console.log(">>>>>>>>>>>>>>>> Encoder types");
|
|
3382
3477
|
// this.context.schemas.forEach((id, schema) => {
|
|
3383
3478
|
// console.log("type:", id, schema.name, Object.keys(schema[Symbol.metadata]));
|
|
3384
3479
|
// });
|
|
3385
3480
|
}
|
|
3386
|
-
|
|
3387
|
-
this.root = new Root();
|
|
3481
|
+
setState(state) {
|
|
3388
3482
|
this.state = state;
|
|
3389
|
-
state[$changes].setRoot(this.root);
|
|
3483
|
+
this.state[$changes].setRoot(this.root);
|
|
3390
3484
|
}
|
|
3391
|
-
encode(it = { offset: 0 }, view,
|
|
3392
|
-
|
|
3393
|
-
const isEncodeAll = this.root.allChanges === changeTrees;
|
|
3485
|
+
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
|
|
3486
|
+
) {
|
|
3394
3487
|
const hasView = (view !== undefined);
|
|
3395
3488
|
const rootChangeTree = this.state[$changes];
|
|
3396
3489
|
const changeTreesIterator = changeTrees.entries();
|
|
@@ -3399,6 +3492,12 @@
|
|
|
3399
3492
|
const ctor = ref['constructor'];
|
|
3400
3493
|
const encoder = ctor[$encoder];
|
|
3401
3494
|
const filter = ctor[$filter];
|
|
3495
|
+
// try { throw new Error(); } catch (e) {
|
|
3496
|
+
// // only print if not coming from Reflection.ts
|
|
3497
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
3498
|
+
// console.log("ChangeTree:", { ref: ref.constructor.name, });
|
|
3499
|
+
// }
|
|
3500
|
+
// }
|
|
3402
3501
|
if (hasView) {
|
|
3403
3502
|
if (!view.items.has(changeTree)) {
|
|
3404
3503
|
view.invisible.add(changeTree);
|
|
@@ -3409,9 +3508,10 @@
|
|
|
3409
3508
|
}
|
|
3410
3509
|
}
|
|
3411
3510
|
// skip root `refId` if it's the first change tree
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3511
|
+
// (unless it "hasView", which will need to revisit the root)
|
|
3512
|
+
if (hasView || it.offset > initialOffset || changeTree !== rootChangeTree) {
|
|
3513
|
+
buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
|
|
3514
|
+
number$1(buffer, changeTree.refId, it);
|
|
3415
3515
|
}
|
|
3416
3516
|
const changesIterator = changes.entries();
|
|
3417
3517
|
for (const [fieldIndex, operation] of changesIterator) {
|
|
@@ -3423,27 +3523,39 @@
|
|
|
3423
3523
|
// TODO: avoid checking if no view tags were defined
|
|
3424
3524
|
//
|
|
3425
3525
|
if (filter && !filter(ref, fieldIndex, view)) {
|
|
3426
|
-
// console.log("SKIP FIELD:", { ref: changeTree.ref.constructor.name, fieldIndex, })
|
|
3427
3526
|
// console.log("ADD AS INVISIBLE:", fieldIndex, changeTree.ref.constructor.name)
|
|
3428
3527
|
// view?.invisible.add(changeTree);
|
|
3429
3528
|
continue;
|
|
3430
3529
|
}
|
|
3431
|
-
//
|
|
3432
|
-
//
|
|
3433
|
-
//
|
|
3434
|
-
//
|
|
3435
|
-
//
|
|
3436
|
-
|
|
3530
|
+
// try { throw new Error(); } catch (e) {
|
|
3531
|
+
// // only print if not coming from Reflection.ts
|
|
3532
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
3533
|
+
// console.log("WILL ENCODE", {
|
|
3534
|
+
// ref: changeTree.ref.constructor.name,
|
|
3535
|
+
// fieldIndex,
|
|
3536
|
+
// operation: OPERATION[operation],
|
|
3537
|
+
// });
|
|
3538
|
+
// }
|
|
3539
|
+
// }
|
|
3540
|
+
encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
|
|
3437
3541
|
}
|
|
3438
3542
|
}
|
|
3439
|
-
if (it.offset >
|
|
3440
|
-
const newSize = getNextPowerOf2(
|
|
3441
|
-
console.warn(
|
|
3543
|
+
if (it.offset > buffer.byteLength) {
|
|
3544
|
+
const newSize = getNextPowerOf2(buffer.byteLength * 2);
|
|
3545
|
+
console.warn(`@colyseus/schema buffer overflow. Encoded state is higher than default BUFFER_SIZE. Use the following to increase default BUFFER_SIZE:
|
|
3546
|
+
|
|
3547
|
+
import { Encoder } from "@colyseus/schema";
|
|
3548
|
+
Encoder.BUFFER_SIZE = ${Math.round(newSize / 1024)} * 1024; // ${Math.round(newSize / 1024)} KB
|
|
3549
|
+
`);
|
|
3442
3550
|
//
|
|
3443
3551
|
// resize buffer and re-encode (TODO: can we avoid re-encoding here?)
|
|
3444
3552
|
//
|
|
3445
|
-
|
|
3446
|
-
|
|
3553
|
+
buffer = Buffer.allocUnsafeSlow(newSize);
|
|
3554
|
+
// assign resized buffer to local sharedBuffer
|
|
3555
|
+
if (buffer === this.sharedBuffer) {
|
|
3556
|
+
this.sharedBuffer = buffer;
|
|
3557
|
+
}
|
|
3558
|
+
return this.encode({ offset: initialOffset }, view, buffer, changeTrees, isEncodeAll);
|
|
3447
3559
|
}
|
|
3448
3560
|
else {
|
|
3449
3561
|
//
|
|
@@ -3455,42 +3567,47 @@
|
|
|
3455
3567
|
//
|
|
3456
3568
|
this.onEndEncode(changeTrees);
|
|
3457
3569
|
}
|
|
3458
|
-
|
|
3459
|
-
return bytes.slice(0, it.offset);
|
|
3570
|
+
return buffer.subarray(0, it.offset);
|
|
3460
3571
|
}
|
|
3461
3572
|
}
|
|
3462
|
-
encodeAll(it = { offset: 0 }) {
|
|
3463
|
-
// console.log(
|
|
3464
|
-
//
|
|
3465
|
-
|
|
3466
|
-
// });
|
|
3467
|
-
return this.encode(it, undefined, this.sharedBuffer, this.root.allChanges);
|
|
3573
|
+
encodeAll(it = { offset: 0 }, buffer = this.sharedBuffer) {
|
|
3574
|
+
// console.log(`\nencodeAll(), this.root.allChanges (${this.root.allChanges.size})`);
|
|
3575
|
+
// this.debugChanges("allChanges");
|
|
3576
|
+
return this.encode(it, undefined, buffer, this.root.allChanges, true);
|
|
3468
3577
|
}
|
|
3469
3578
|
encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3470
3579
|
const viewOffset = it.offset;
|
|
3471
|
-
// console.log(
|
|
3472
|
-
// this.
|
|
3580
|
+
// console.log(`\nencodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
|
|
3581
|
+
// this.debugChanges("allFilteredChanges");
|
|
3473
3582
|
// try to encode "filtered" changes
|
|
3474
|
-
this.encode(it, view, bytes, this.root.allFilteredChanges);
|
|
3583
|
+
this.encode(it, view, bytes, this.root.allFilteredChanges, true, viewOffset);
|
|
3475
3584
|
return Buffer.concat([
|
|
3476
|
-
bytes.
|
|
3477
|
-
bytes.
|
|
3585
|
+
bytes.subarray(0, sharedOffset),
|
|
3586
|
+
bytes.subarray(viewOffset, it.offset)
|
|
3478
3587
|
]);
|
|
3479
3588
|
}
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3589
|
+
debugChanges(field) {
|
|
3590
|
+
const changeSet = (typeof (field) === "string")
|
|
3591
|
+
? this.root[field]
|
|
3592
|
+
: field;
|
|
3593
|
+
Array.from(changeSet.entries()).map((item) => {
|
|
3594
|
+
const metadata = item[0].ref.constructor[Symbol.metadata];
|
|
3595
|
+
console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
|
|
3596
|
+
item[1].forEach((op, index) => {
|
|
3597
|
+
console.log(" ->", {
|
|
3598
|
+
index,
|
|
3599
|
+
field: metadata?.[index],
|
|
3600
|
+
op: exports.OPERATION[op],
|
|
3601
|
+
});
|
|
3602
|
+
});
|
|
3603
|
+
});
|
|
3604
|
+
}
|
|
3490
3605
|
encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3491
3606
|
const viewOffset = it.offset;
|
|
3492
|
-
//
|
|
3493
|
-
this.
|
|
3607
|
+
// console.log(`\nencodeView(), view.changes (${view.changes.size})`);
|
|
3608
|
+
// this.debugChanges(view.changes);
|
|
3609
|
+
// console.log(`\nencodeView(), this.root.filteredChanges (${this.root.filteredChanges.size})`);
|
|
3610
|
+
// this.debugChanges("filteredChanges");
|
|
3494
3611
|
// encode visibility changes (add/remove for this view)
|
|
3495
3612
|
const viewChangesIterator = view.changes.entries();
|
|
3496
3613
|
for (const [changeTree, changes] of viewChangesIterator) {
|
|
@@ -3517,9 +3634,11 @@
|
|
|
3517
3634
|
//
|
|
3518
3635
|
// clear "view" changes after encoding
|
|
3519
3636
|
view.changes.clear();
|
|
3637
|
+
// try to encode "filtered" changes
|
|
3638
|
+
this.encode(it, view, bytes, this.root.filteredChanges, false, viewOffset);
|
|
3520
3639
|
return Buffer.concat([
|
|
3521
|
-
bytes.
|
|
3522
|
-
bytes.
|
|
3640
|
+
bytes.subarray(0, sharedOffset),
|
|
3641
|
+
bytes.subarray(viewOffset, it.offset)
|
|
3523
3642
|
]);
|
|
3524
3643
|
}
|
|
3525
3644
|
onEndEncode(changeTrees = this.root.changes) {
|
|
@@ -3689,21 +3808,21 @@
|
|
|
3689
3808
|
class Decoder {
|
|
3690
3809
|
constructor(root, context) {
|
|
3691
3810
|
this.currentRefId = 0;
|
|
3692
|
-
this.
|
|
3811
|
+
this.setState(root);
|
|
3693
3812
|
this.context = context || new TypeContext(root.constructor);
|
|
3694
3813
|
// console.log(">>>>>>>>>>>>>>>> Decoder types");
|
|
3695
3814
|
// this.context.schemas.forEach((id, schema) => {
|
|
3696
3815
|
// console.log("type:", id, schema.name, Object.keys(schema[Symbol.metadata]));
|
|
3697
3816
|
// });
|
|
3698
3817
|
}
|
|
3699
|
-
|
|
3818
|
+
setState(root) {
|
|
3700
3819
|
this.state = root;
|
|
3701
|
-
this
|
|
3702
|
-
this
|
|
3820
|
+
this.root = new ReferenceTracker();
|
|
3821
|
+
this.root.addRef(0, root);
|
|
3703
3822
|
}
|
|
3704
3823
|
decode(bytes, it = { offset: 0 }, ref = this.state) {
|
|
3705
3824
|
const allChanges = [];
|
|
3706
|
-
const $root = this
|
|
3825
|
+
const $root = this.root;
|
|
3707
3826
|
const totalBytes = bytes.byteLength;
|
|
3708
3827
|
let decoder = ref['constructor'][$decoder];
|
|
3709
3828
|
this.currentRefId = 0;
|
|
@@ -3784,7 +3903,7 @@
|
|
|
3784
3903
|
previousValue: value
|
|
3785
3904
|
});
|
|
3786
3905
|
if (needRemoveRef) {
|
|
3787
|
-
this
|
|
3906
|
+
this.root.removeRef(this.root.refIds.get(value));
|
|
3788
3907
|
}
|
|
3789
3908
|
});
|
|
3790
3909
|
}
|
|
@@ -3824,10 +3943,8 @@
|
|
|
3824
3943
|
super(...arguments);
|
|
3825
3944
|
this.types = new ArraySchema();
|
|
3826
3945
|
}
|
|
3827
|
-
static encode(instance, context) {
|
|
3828
|
-
|
|
3829
|
-
context = new TypeContext(instance.constructor);
|
|
3830
|
-
}
|
|
3946
|
+
static encode(instance, context, it = { offset: 0 }) {
|
|
3947
|
+
context ??= new TypeContext(instance.constructor);
|
|
3831
3948
|
const reflection = new Reflection();
|
|
3832
3949
|
const encoder = new Encoder(reflection);
|
|
3833
3950
|
const buildType = (currentType, metadata) => {
|
|
@@ -3881,7 +3998,6 @@
|
|
|
3881
3998
|
}
|
|
3882
3999
|
buildType(type, klass[Symbol.metadata]);
|
|
3883
4000
|
}
|
|
3884
|
-
const it = { offset: 0 };
|
|
3885
4001
|
const buf = encoder.encodeAll(it);
|
|
3886
4002
|
return Buffer.from(buf, 0, it.offset);
|
|
3887
4003
|
}
|
|
@@ -3889,59 +4005,314 @@
|
|
|
3889
4005
|
const reflection = new Reflection();
|
|
3890
4006
|
const reflectionDecoder = new Decoder(reflection);
|
|
3891
4007
|
reflectionDecoder.decode(bytes, it);
|
|
3892
|
-
const
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
const
|
|
4008
|
+
const typeContext = new TypeContext();
|
|
4009
|
+
// 1st pass, initialize metadata + inheritance
|
|
4010
|
+
reflection.types.forEach((reflectionType) => {
|
|
4011
|
+
const parentClass = typeContext.get(reflectionType.extendsId) ?? Schema;
|
|
4012
|
+
const schema = class _ extends parentClass {
|
|
3896
4013
|
};
|
|
3897
|
-
|
|
3898
|
-
const _metadata = parentKlass && parentKlass[Symbol.metadata] || Object.create(null);
|
|
3899
|
-
Object.defineProperty(schema, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
4014
|
+
const parentMetadata = parentClass[Symbol.metadata];
|
|
3900
4015
|
// register for inheritance support
|
|
3901
4016
|
TypeContext.register(schema);
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
return types;
|
|
4017
|
+
// for inheritance support
|
|
4018
|
+
Metadata.initialize(schema, parentMetadata);
|
|
4019
|
+
typeContext.add(schema, reflectionType.id);
|
|
3906
4020
|
}, {});
|
|
4021
|
+
// 2nd pass, set fields
|
|
3907
4022
|
reflection.types.forEach((reflectionType) => {
|
|
3908
|
-
const schemaType =
|
|
4023
|
+
const schemaType = typeContext.get(reflectionType.id);
|
|
3909
4024
|
const metadata = schemaType[Symbol.metadata];
|
|
3910
|
-
|
|
3911
|
-
const parentFieldIndex =
|
|
4025
|
+
// FIXME: use metadata[-1] to get field count
|
|
4026
|
+
const parentFieldIndex = 0;
|
|
4027
|
+
// console.log("--------------------");
|
|
4028
|
+
// // console.log("reflectionType", reflectionType.toJSON());
|
|
4029
|
+
// console.log("reflectionType.fields", reflectionType.fields.toJSON());
|
|
4030
|
+
// console.log("parentFieldIndex", parentFieldIndex);
|
|
4031
|
+
//
|
|
4032
|
+
// FIXME: set fields using parentKlass as well
|
|
4033
|
+
// currently the fields are duplicated on inherited classes
|
|
4034
|
+
//
|
|
4035
|
+
// // const parentKlass = reflection.types[reflectionType.extendsId];
|
|
4036
|
+
// // parentKlass.fields
|
|
3912
4037
|
reflectionType.fields.forEach((field, i) => {
|
|
3913
4038
|
const fieldIndex = parentFieldIndex + i;
|
|
3914
4039
|
if (field.referencedType !== undefined) {
|
|
3915
4040
|
let fieldType = field.type;
|
|
3916
|
-
let refType =
|
|
4041
|
+
let refType = typeContext.get(field.referencedType);
|
|
3917
4042
|
// map or array of primitive type (-1)
|
|
3918
4043
|
if (!refType) {
|
|
3919
4044
|
const typeInfo = field.type.split(":");
|
|
3920
4045
|
fieldType = typeInfo[0];
|
|
3921
|
-
refType = typeInfo[1];
|
|
4046
|
+
refType = typeInfo[1]; // string
|
|
3922
4047
|
}
|
|
3923
4048
|
if (fieldType === "ref") {
|
|
3924
|
-
// type(refType)(schemaType.prototype, field.name);
|
|
3925
4049
|
Metadata.addField(metadata, fieldIndex, field.name, refType);
|
|
3926
4050
|
}
|
|
3927
4051
|
else {
|
|
3928
|
-
// type({ [fieldType]: refType } as DefinitionType)(schemaType.prototype, field.name);
|
|
3929
4052
|
Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType });
|
|
3930
4053
|
}
|
|
3931
4054
|
}
|
|
3932
4055
|
else {
|
|
3933
|
-
// type(field.type as PrimitiveType)(schemaType.prototype, field.name);
|
|
3934
4056
|
Metadata.addField(metadata, fieldIndex, field.name, field.type);
|
|
3935
4057
|
}
|
|
3936
4058
|
});
|
|
3937
4059
|
});
|
|
3938
|
-
|
|
4060
|
+
// @ts-ignore
|
|
4061
|
+
return new (typeContext.get(0))();
|
|
3939
4062
|
}
|
|
3940
4063
|
}
|
|
3941
4064
|
__decorate([
|
|
3942
4065
|
type([ReflectionType])
|
|
3943
4066
|
], Reflection.prototype, "types", void 0);
|
|
3944
4067
|
|
|
4068
|
+
function getDecoderStateCallbacks(decoder) {
|
|
4069
|
+
const $root = decoder.root;
|
|
4070
|
+
const callbacks = $root.callbacks;
|
|
4071
|
+
const onAddCalls = new WeakMap();
|
|
4072
|
+
let currentOnAddCallback;
|
|
4073
|
+
decoder.triggerChanges = function (allChanges) {
|
|
4074
|
+
const uniqueRefIds = new Set();
|
|
4075
|
+
for (let i = 0, l = allChanges.length; i < l; i++) {
|
|
4076
|
+
const change = allChanges[i];
|
|
4077
|
+
const refId = change.refId;
|
|
4078
|
+
const ref = change.ref;
|
|
4079
|
+
const $callbacks = callbacks[refId];
|
|
4080
|
+
if (!$callbacks) {
|
|
4081
|
+
continue;
|
|
4082
|
+
}
|
|
4083
|
+
//
|
|
4084
|
+
// trigger onRemove on child structure.
|
|
4085
|
+
//
|
|
4086
|
+
if ((change.op & exports.OPERATION.DELETE) === exports.OPERATION.DELETE &&
|
|
4087
|
+
change.previousValue instanceof Schema) {
|
|
4088
|
+
const deleteCallbacks = callbacks[$root.refIds.get(change.previousValue)]?.[exports.OPERATION.DELETE];
|
|
4089
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
4090
|
+
deleteCallbacks[i]();
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
if (ref instanceof Schema) {
|
|
4094
|
+
//
|
|
4095
|
+
// Handle schema instance
|
|
4096
|
+
//
|
|
4097
|
+
if (!uniqueRefIds.has(refId)) {
|
|
4098
|
+
// trigger onChange
|
|
4099
|
+
const replaceCallbacks = $callbacks?.[exports.OPERATION.REPLACE];
|
|
4100
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
4101
|
+
replaceCallbacks[i]();
|
|
4102
|
+
// try {
|
|
4103
|
+
// } catch (e) {
|
|
4104
|
+
// console.error(e);
|
|
4105
|
+
// }
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
if ($callbacks.hasOwnProperty(change.field)) {
|
|
4109
|
+
const fieldCallbacks = $callbacks[change.field];
|
|
4110
|
+
for (let i = fieldCallbacks?.length - 1; i >= 0; i--) {
|
|
4111
|
+
fieldCallbacks[i](change.value, change.previousValue);
|
|
4112
|
+
// try {
|
|
4113
|
+
// } catch (e) {
|
|
4114
|
+
// console.error(e);
|
|
4115
|
+
// }
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
else {
|
|
4120
|
+
//
|
|
4121
|
+
// Handle collection of items
|
|
4122
|
+
//
|
|
4123
|
+
if ((change.op & exports.OPERATION.DELETE) === exports.OPERATION.DELETE) {
|
|
4124
|
+
//
|
|
4125
|
+
// FIXME: `previousValue` should always be available.
|
|
4126
|
+
//
|
|
4127
|
+
if (change.previousValue !== undefined) {
|
|
4128
|
+
// triger onRemove
|
|
4129
|
+
const deleteCallbacks = $callbacks[exports.OPERATION.DELETE];
|
|
4130
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
4131
|
+
deleteCallbacks[i](change.previousValue, change.dynamicIndex ?? change.field);
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
// Handle DELETE_AND_ADD operations
|
|
4135
|
+
if ((change.op & exports.OPERATION.ADD) === exports.OPERATION.ADD) {
|
|
4136
|
+
const addCallbacks = $callbacks[exports.OPERATION.ADD];
|
|
4137
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4138
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4139
|
+
}
|
|
4140
|
+
}
|
|
4141
|
+
}
|
|
4142
|
+
else if ((change.op & exports.OPERATION.ADD) === exports.OPERATION.ADD && change.previousValue === undefined) {
|
|
4143
|
+
// triger onAdd
|
|
4144
|
+
const addCallbacks = $callbacks[exports.OPERATION.ADD];
|
|
4145
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4146
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
// trigger onChange
|
|
4150
|
+
if (change.value !== change.previousValue) {
|
|
4151
|
+
const replaceCallbacks = $callbacks[exports.OPERATION.REPLACE];
|
|
4152
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
4153
|
+
replaceCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
uniqueRefIds.add(refId);
|
|
4158
|
+
}
|
|
4159
|
+
};
|
|
4160
|
+
function getProxy(metadataOrType, context) {
|
|
4161
|
+
let metadata = context.instance?.constructor[Symbol.metadata] || metadataOrType;
|
|
4162
|
+
let isCollection = ((context.instance && typeof (context.instance['forEach']) === "function") ||
|
|
4163
|
+
(metadataOrType && typeof (metadataOrType[Symbol.metadata]) === "undefined"));
|
|
4164
|
+
if (metadata && !isCollection) {
|
|
4165
|
+
const onAddListen = function (ref, prop, callback, immediate) {
|
|
4166
|
+
// immediate trigger
|
|
4167
|
+
if (immediate &&
|
|
4168
|
+
context.instance[prop] !== undefined &&
|
|
4169
|
+
!onAddCalls.has(currentOnAddCallback) // Workaround for https://github.com/colyseus/schema/issues/147
|
|
4170
|
+
) {
|
|
4171
|
+
callback(context.instance[prop], undefined);
|
|
4172
|
+
}
|
|
4173
|
+
return $root.addCallback($root.refIds.get(ref), prop, callback);
|
|
4174
|
+
};
|
|
4175
|
+
/**
|
|
4176
|
+
* Schema instances
|
|
4177
|
+
*/
|
|
4178
|
+
return new Proxy({
|
|
4179
|
+
listen: function listen(prop, callback, immediate = true) {
|
|
4180
|
+
if (context.instance) {
|
|
4181
|
+
return onAddListen(context.instance, prop, callback, immediate);
|
|
4182
|
+
}
|
|
4183
|
+
else {
|
|
4184
|
+
// collection instance not received yet
|
|
4185
|
+
let detachCallback = () => { };
|
|
4186
|
+
context.onInstanceAvailable((ref, existing) => {
|
|
4187
|
+
detachCallback = onAddListen(ref, prop, callback, immediate && existing && !onAddCalls.has(currentOnAddCallback));
|
|
4188
|
+
});
|
|
4189
|
+
return () => detachCallback();
|
|
4190
|
+
}
|
|
4191
|
+
},
|
|
4192
|
+
onChange: function onChange(callback) {
|
|
4193
|
+
return $root.addCallback($root.refIds.get(context.instance), exports.OPERATION.REPLACE, callback);
|
|
4194
|
+
},
|
|
4195
|
+
//
|
|
4196
|
+
// TODO: refactor `bindTo()` implementation.
|
|
4197
|
+
// There is room for improvement.
|
|
4198
|
+
//
|
|
4199
|
+
bindTo: function bindTo(targetObject, properties) {
|
|
4200
|
+
if (!properties) {
|
|
4201
|
+
properties = Object.keys(metadata);
|
|
4202
|
+
}
|
|
4203
|
+
return $root.addCallback($root.refIds.get(context.instance), exports.OPERATION.REPLACE, () => {
|
|
4204
|
+
properties.forEach((prop) => targetObject[prop] = context.instance[prop]);
|
|
4205
|
+
});
|
|
4206
|
+
}
|
|
4207
|
+
}, {
|
|
4208
|
+
get(target, prop) {
|
|
4209
|
+
if (metadata[prop]) {
|
|
4210
|
+
const instance = context.instance?.[prop];
|
|
4211
|
+
const onInstanceAvailable = ((callback) => {
|
|
4212
|
+
const unbind = $(context.instance).listen(prop, (value, _) => {
|
|
4213
|
+
callback(value, false);
|
|
4214
|
+
// FIXME: by "unbinding" the callback here,
|
|
4215
|
+
// it will not support when the server
|
|
4216
|
+
// re-instantiates the instance.
|
|
4217
|
+
//
|
|
4218
|
+
unbind?.();
|
|
4219
|
+
}, false);
|
|
4220
|
+
// has existing value
|
|
4221
|
+
if ($root.refIds.get(instance) !== undefined) {
|
|
4222
|
+
callback(instance, true);
|
|
4223
|
+
}
|
|
4224
|
+
});
|
|
4225
|
+
return getProxy(metadata[prop].type, {
|
|
4226
|
+
// make sure refId is available, otherwise need to wait for the instance to be available.
|
|
4227
|
+
instance: ($root.refIds.get(instance) && instance),
|
|
4228
|
+
parentInstance: context.instance,
|
|
4229
|
+
onInstanceAvailable,
|
|
4230
|
+
});
|
|
4231
|
+
}
|
|
4232
|
+
else {
|
|
4233
|
+
// accessing the function
|
|
4234
|
+
return target[prop];
|
|
4235
|
+
}
|
|
4236
|
+
},
|
|
4237
|
+
has(target, prop) { return metadata[prop] !== undefined; },
|
|
4238
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4239
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4240
|
+
});
|
|
4241
|
+
}
|
|
4242
|
+
else {
|
|
4243
|
+
/**
|
|
4244
|
+
* Collection instances
|
|
4245
|
+
*/
|
|
4246
|
+
const onAdd = function (ref, callback, immediate) {
|
|
4247
|
+
// Trigger callback on existing items
|
|
4248
|
+
if (immediate) {
|
|
4249
|
+
ref.forEach((v, k) => callback(v, k));
|
|
4250
|
+
}
|
|
4251
|
+
return $root.addCallback($root.refIds.get(ref), exports.OPERATION.ADD, (value, key) => {
|
|
4252
|
+
onAddCalls.set(callback, true);
|
|
4253
|
+
currentOnAddCallback = callback;
|
|
4254
|
+
callback(value, key);
|
|
4255
|
+
onAddCalls.delete(callback);
|
|
4256
|
+
currentOnAddCallback = undefined;
|
|
4257
|
+
});
|
|
4258
|
+
};
|
|
4259
|
+
const onRemove = function (ref, callback) {
|
|
4260
|
+
return $root.addCallback($root.refIds.get(ref), exports.OPERATION.DELETE, callback);
|
|
4261
|
+
};
|
|
4262
|
+
return new Proxy({
|
|
4263
|
+
onAdd: function (callback, immediate = true) {
|
|
4264
|
+
//
|
|
4265
|
+
// https://github.com/colyseus/schema/issues/147
|
|
4266
|
+
// If parent instance has "onAdd" registered, avoid triggering immediate callback.
|
|
4267
|
+
//
|
|
4268
|
+
if (context.instance) {
|
|
4269
|
+
return onAdd(context.instance, callback, immediate && !onAddCalls.has(currentOnAddCallback));
|
|
4270
|
+
}
|
|
4271
|
+
else if (context.onInstanceAvailable) {
|
|
4272
|
+
// collection instance not received yet
|
|
4273
|
+
let detachCallback = () => { };
|
|
4274
|
+
context.onInstanceAvailable((ref, existing) => {
|
|
4275
|
+
detachCallback = onAdd(ref, callback, immediate && existing && !onAddCalls.has(currentOnAddCallback));
|
|
4276
|
+
});
|
|
4277
|
+
return () => detachCallback();
|
|
4278
|
+
}
|
|
4279
|
+
},
|
|
4280
|
+
onRemove: function (callback) {
|
|
4281
|
+
if (context.onInstanceAvailable) {
|
|
4282
|
+
// collection instance not received yet
|
|
4283
|
+
let detachCallback = () => { };
|
|
4284
|
+
context.onInstanceAvailable((ref) => {
|
|
4285
|
+
detachCallback = onRemove(ref, callback);
|
|
4286
|
+
});
|
|
4287
|
+
return () => detachCallback();
|
|
4288
|
+
}
|
|
4289
|
+
else if (context.instance) {
|
|
4290
|
+
return onRemove(context.instance, callback);
|
|
4291
|
+
}
|
|
4292
|
+
},
|
|
4293
|
+
}, {
|
|
4294
|
+
get(target, prop) {
|
|
4295
|
+
if (!target[prop]) {
|
|
4296
|
+
throw new Error(`Can't access '${prop}' through callback proxy. access the instance directly.`);
|
|
4297
|
+
}
|
|
4298
|
+
return target[prop];
|
|
4299
|
+
},
|
|
4300
|
+
has(target, prop) { return target[prop] !== undefined; },
|
|
4301
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4302
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4303
|
+
});
|
|
4304
|
+
}
|
|
4305
|
+
}
|
|
4306
|
+
function $(instance) {
|
|
4307
|
+
return getProxy(undefined, { instance });
|
|
4308
|
+
}
|
|
4309
|
+
return $;
|
|
4310
|
+
}
|
|
4311
|
+
|
|
4312
|
+
function getRawChangesCallback(decoder, callback) {
|
|
4313
|
+
decoder.triggerChanges = callback;
|
|
4314
|
+
}
|
|
4315
|
+
|
|
3945
4316
|
class StateView {
|
|
3946
4317
|
constructor() {
|
|
3947
4318
|
/**
|
|
@@ -3959,20 +4330,21 @@
|
|
|
3959
4330
|
this.changes = new Map();
|
|
3960
4331
|
}
|
|
3961
4332
|
// TODO: allow to set multiple tags at once
|
|
3962
|
-
add(obj, tag = DEFAULT_VIEW_TAG) {
|
|
4333
|
+
add(obj, tag = DEFAULT_VIEW_TAG, checkIncludeParent = true) {
|
|
3963
4334
|
if (!obj[$changes]) {
|
|
3964
4335
|
console.warn("StateView#add(), invalid object:", obj);
|
|
3965
4336
|
return this;
|
|
3966
4337
|
}
|
|
3967
|
-
let changeTree = obj[$changes];
|
|
3968
|
-
this.items.add(changeTree);
|
|
3969
|
-
// Add children of this ChangeTree to this view
|
|
3970
|
-
changeTree.forEachChild((change, _) => this.add(change.ref, tag));
|
|
3971
4338
|
// FIXME: ArraySchema/MapSchema does not have metadata
|
|
3972
4339
|
const metadata = obj.constructor[Symbol.metadata];
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
4340
|
+
const changeTree = obj[$changes];
|
|
4341
|
+
this.items.add(changeTree);
|
|
4342
|
+
// add parent ChangeTree's
|
|
4343
|
+
// - if it was invisible to this view
|
|
4344
|
+
// - if it were previously filtered out
|
|
4345
|
+
if (checkIncludeParent && changeTree.parent) {
|
|
4346
|
+
this.addParent(changeTree.parent[$changes], changeTree.parentIndex, tag);
|
|
4347
|
+
}
|
|
3976
4348
|
//
|
|
3977
4349
|
// TODO: when adding an item of a MapSchema, the changes may not
|
|
3978
4350
|
// be set (only the parent's changes are set)
|
|
@@ -3996,7 +4368,6 @@
|
|
|
3996
4368
|
tags = this.tags.get(changeTree);
|
|
3997
4369
|
}
|
|
3998
4370
|
tags.add(tag);
|
|
3999
|
-
// console.log("BY TAG:", tag);
|
|
4000
4371
|
// Ref: add tagged properties
|
|
4001
4372
|
metadata?.[-3]?.[tag]?.forEach((index) => {
|
|
4002
4373
|
if (changeTree.getChange(index) !== exports.OPERATION.DELETE) {
|
|
@@ -4005,73 +4376,63 @@
|
|
|
4005
4376
|
});
|
|
4006
4377
|
}
|
|
4007
4378
|
else {
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
// metadata?.[-3]?.[DEFAULT_VIEW_TAG]?.forEach((index) => {
|
|
4011
|
-
// if (changeTree.getChange(index) !== OPERATION.DELETE) {
|
|
4012
|
-
// changes.set(index, OPERATION.ADD);
|
|
4013
|
-
// }
|
|
4014
|
-
// });
|
|
4015
|
-
const allChangesSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
|
|
4379
|
+
const isInvisible = this.invisible.has(changeTree);
|
|
4380
|
+
const changeSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
|
|
4016
4381
|
? changeTree.allFilteredChanges
|
|
4017
4382
|
: changeTree.allChanges;
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4383
|
+
changeSet.forEach((op, index) => {
|
|
4384
|
+
const tagAtIndex = metadata?.[metadata?.[index]].tag;
|
|
4385
|
+
if ((isInvisible || // if "invisible", include all
|
|
4386
|
+
tagAtIndex === undefined || // "all change" with no tag
|
|
4387
|
+
tagAtIndex === tag // tagged property
|
|
4388
|
+
) &&
|
|
4389
|
+
op !== exports.OPERATION.DELETE) {
|
|
4390
|
+
changes.set(index, op);
|
|
4024
4391
|
}
|
|
4025
|
-
}
|
|
4026
|
-
}
|
|
4027
|
-
// TODO: avoid unnecessary iteration here
|
|
4028
|
-
while (changeTree.parent &&
|
|
4029
|
-
(changeTree = changeTree.parent[$changes]) &&
|
|
4030
|
-
(changeTree.isFiltered || changeTree.isPartiallyFiltered)) {
|
|
4031
|
-
this.items.add(changeTree);
|
|
4392
|
+
});
|
|
4032
4393
|
}
|
|
4394
|
+
// Add children of this ChangeTree to this view
|
|
4395
|
+
changeTree.forEachChild((change, index) => {
|
|
4396
|
+
// Do not ADD children that don't have the same tag
|
|
4397
|
+
if (metadata && metadata[metadata[index]].tag !== tag) {
|
|
4398
|
+
return;
|
|
4399
|
+
}
|
|
4400
|
+
this.add(change.ref, tag, false);
|
|
4401
|
+
});
|
|
4033
4402
|
return this;
|
|
4034
4403
|
}
|
|
4035
|
-
addParent(changeTree, tag) {
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4404
|
+
addParent(changeTree, parentIndex, tag) {
|
|
4405
|
+
// view must have all "changeTree" parent tree
|
|
4406
|
+
this.items.add(changeTree);
|
|
4407
|
+
// add parent's parent
|
|
4408
|
+
const parentChangeTree = changeTree.parent?.[$changes];
|
|
4409
|
+
if (parentChangeTree && (parentChangeTree.isFiltered || parentChangeTree.isPartiallyFiltered)) {
|
|
4410
|
+
this.addParent(parentChangeTree, changeTree.parentIndex, tag);
|
|
4039
4411
|
}
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
if (!this.invisible.has(parentChangeTree)) {
|
|
4043
|
-
// parent is already available, no need to add it!
|
|
4412
|
+
// parent is already available, no need to add it!
|
|
4413
|
+
if (!this.invisible.has(changeTree)) {
|
|
4044
4414
|
return;
|
|
4045
4415
|
}
|
|
4046
|
-
this.addParent(parentChangeTree, tag);
|
|
4047
4416
|
// add parent's tag properties
|
|
4048
|
-
if (
|
|
4049
|
-
let
|
|
4050
|
-
if (
|
|
4051
|
-
|
|
4052
|
-
this.changes.set(
|
|
4417
|
+
if (changeTree.getChange(parentIndex) !== exports.OPERATION.DELETE) {
|
|
4418
|
+
let changes = this.changes.get(changeTree);
|
|
4419
|
+
if (changes === undefined) {
|
|
4420
|
+
changes = new Map();
|
|
4421
|
+
this.changes.set(changeTree, changes);
|
|
4053
4422
|
}
|
|
4054
|
-
// console.log("add parent change", {
|
|
4055
|
-
// parentIndex,
|
|
4056
|
-
// parentChanges,
|
|
4057
|
-
// parentChange: (
|
|
4058
|
-
// parentChangeTree.getChange(parentIndex) &&
|
|
4059
|
-
// OPERATION[parentChangeTree.getChange(parentIndex)]
|
|
4060
|
-
// ),
|
|
4061
|
-
// })
|
|
4062
4423
|
if (!this.tags) {
|
|
4063
4424
|
this.tags = new WeakMap();
|
|
4064
4425
|
}
|
|
4065
4426
|
let tags;
|
|
4066
|
-
if (!this.tags.has(
|
|
4427
|
+
if (!this.tags.has(changeTree)) {
|
|
4067
4428
|
tags = new Set();
|
|
4068
|
-
this.tags.set(
|
|
4429
|
+
this.tags.set(changeTree, tags);
|
|
4069
4430
|
}
|
|
4070
4431
|
else {
|
|
4071
|
-
tags = this.tags.get(
|
|
4432
|
+
tags = this.tags.get(changeTree);
|
|
4072
4433
|
}
|
|
4073
4434
|
tags.add(tag);
|
|
4074
|
-
|
|
4435
|
+
changes.set(parentIndex, exports.OPERATION.ADD);
|
|
4075
4436
|
}
|
|
4076
4437
|
}
|
|
4077
4438
|
remove(obj, tag = DEFAULT_VIEW_TAG) {
|
|
@@ -4166,10 +4527,10 @@
|
|
|
4166
4527
|
exports.encode = encode;
|
|
4167
4528
|
exports.encodeKeyValueOperation = encodeArray;
|
|
4168
4529
|
exports.encodeSchemaOperation = encodeSchemaOperation;
|
|
4530
|
+
exports.getDecoderStateCallbacks = getDecoderStateCallbacks;
|
|
4531
|
+
exports.getRawChangesCallback = getRawChangesCallback;
|
|
4169
4532
|
exports.registerType = registerType;
|
|
4170
4533
|
exports.type = type;
|
|
4171
4534
|
exports.view = view;
|
|
4172
4535
|
|
|
4173
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4174
|
-
|
|
4175
4536
|
}));
|