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