@colyseus/schema 3.0.0-alpha.2 → 3.0.0-alpha.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +131 -61
- package/build/cjs/index.js +472 -150
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +471 -149
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +472 -150
- 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 +1 -2
- package/lib/Reflection.js +28 -22
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.d.ts +1 -1
- package/lib/annotations.js +12 -10
- package/lib/annotations.js.map +1 -1
- package/lib/bench_encode.d.ts +1 -0
- package/lib/bench_encode.js +93 -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/decoder/DecodeOperation.d.ts +0 -1
- package/lib/decoder/DecodeOperation.js +22 -7
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/decoder/Decoder.d.ts +1 -2
- package/lib/decoder/Decoder.js +4 -4
- 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 +36 -7
- package/lib/decoder/strategy/StateCallbacks.js +39 -46
- package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
- package/lib/encoder/ChangeTree.js +2 -5
- 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 +5 -4
- package/lib/encoder/Encoder.js +56 -38
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/StateView.js +11 -6
- 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.d.ts +21 -19
- package/lib/encoding/decode.js +24 -25
- 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 +3 -0
- package/lib/index.js +5 -1
- package/lib/index.js.map +1 -1
- package/lib/types/custom/ArraySchema.d.ts +2 -2
- package/lib/types/custom/ArraySchema.js +0 -8
- 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 +31 -22
- package/src/annotations.ts +6 -2
- package/src/bench_encode.ts +66 -0
- package/src/decoder/DecodeOperation.ts +26 -6
- package/src/decoder/Decoder.ts +5 -5
- package/src/decoder/strategy/StateCallbacks.ts +93 -53
- package/src/encoder/ChangeTree.ts +2 -4
- package/src/encoder/EncodeOperation.ts +29 -12
- package/src/encoder/Encoder.ts +65 -41
- package/src/encoder/StateView.ts +10 -7
- package/src/encoding/decode.ts +24 -25
- package/src/encoding/encode.ts +25 -22
- package/src/encoding/spec.ts +1 -0
- package/src/index.ts +5 -0
- package/src/types/custom/ArraySchema.ts +2 -1
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.constructor[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));
|
|
@@ -262,14 +300,12 @@ class ChangeTree {
|
|
|
262
300
|
this.checkIsFiltered(parent, parentIndex);
|
|
263
301
|
if (!this.isFiltered) {
|
|
264
302
|
this.root.changes.set(this, this.changes);
|
|
303
|
+
this.root.allChanges.set(this, this.allChanges);
|
|
265
304
|
}
|
|
266
305
|
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
267
306
|
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
268
307
|
this.root.allFilteredChanges.set(this, this.filteredChanges);
|
|
269
308
|
}
|
|
270
|
-
else {
|
|
271
|
-
this.root.allChanges.set(this, this.allChanges);
|
|
272
|
-
}
|
|
273
309
|
this.ensureRefId();
|
|
274
310
|
this.forEachChild((changeTree, atIndex) => {
|
|
275
311
|
changeTree.setParent(this.ref, root, atIndex);
|
|
@@ -361,7 +397,6 @@ class ChangeTree {
|
|
|
361
397
|
}
|
|
362
398
|
_shiftAllChangeIndexes(shiftIndex, startIndex = 0, allChangeSet) {
|
|
363
399
|
Array.from(allChangeSet.entries()).forEach(([index, op]) => {
|
|
364
|
-
// console.log('shiftAllChangeIndexes', index >= startIndex, { index, op, shiftIndex, startIndex })
|
|
365
400
|
if (index >= startIndex) {
|
|
366
401
|
allChangeSet.delete(index);
|
|
367
402
|
allChangeSet.set(index + shiftIndex, op);
|
|
@@ -502,7 +537,7 @@ class ChangeTree {
|
|
|
502
537
|
}
|
|
503
538
|
checkIsFiltered(parent, parentIndex) {
|
|
504
539
|
// Detect if current structure has "filters" declared
|
|
505
|
-
this.isPartiallyFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-2];
|
|
540
|
+
this.isPartiallyFiltered = (this.ref['constructor']?.[Symbol.metadata]?.[-2] !== undefined);
|
|
506
541
|
// TODO: support "partially filtered", where the instance is visible, but only a field is not.
|
|
507
542
|
// Detect if parent has "filters" declared
|
|
508
543
|
while (parent && !this.isFiltered) {
|
|
@@ -563,26 +598,29 @@ try {
|
|
|
563
598
|
textEncoder = new TextEncoder();
|
|
564
599
|
}
|
|
565
600
|
catch (e) { }
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
601
|
+
const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength);
|
|
602
|
+
const utf8Length = (hasBufferByteLength)
|
|
603
|
+
? Buffer.byteLength // node
|
|
604
|
+
: function (str, _) {
|
|
605
|
+
var c = 0, length = 0;
|
|
606
|
+
for (var i = 0, l = str.length; i < l; i++) {
|
|
607
|
+
c = str.charCodeAt(i);
|
|
608
|
+
if (c < 0x80) {
|
|
609
|
+
length += 1;
|
|
610
|
+
}
|
|
611
|
+
else if (c < 0x800) {
|
|
612
|
+
length += 2;
|
|
613
|
+
}
|
|
614
|
+
else if (c < 0xd800 || c >= 0xe000) {
|
|
615
|
+
length += 3;
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
i++;
|
|
619
|
+
length += 4;
|
|
620
|
+
}
|
|
582
621
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
}
|
|
622
|
+
return length;
|
|
623
|
+
};
|
|
586
624
|
function utf8Write(view, str, it) {
|
|
587
625
|
var c = 0;
|
|
588
626
|
for (var i = 0, l = str.length; i < l; i++) {
|
|
@@ -677,8 +715,7 @@ function string$1(bytes, value, it) {
|
|
|
677
715
|
if (!value) {
|
|
678
716
|
value = "";
|
|
679
717
|
}
|
|
680
|
-
|
|
681
|
-
let length = Buffer.byteLength(value, "utf8");
|
|
718
|
+
let length = utf8Length(value, "utf8");
|
|
682
719
|
let size = 0;
|
|
683
720
|
// fixstr
|
|
684
721
|
if (length < 0x20) {
|
|
@@ -789,23 +826,23 @@ function number$1(bytes, value, it) {
|
|
|
789
826
|
|
|
790
827
|
var encode = /*#__PURE__*/Object.freeze({
|
|
791
828
|
__proto__: null,
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
uint8: uint8$1,
|
|
829
|
+
boolean: boolean$1,
|
|
830
|
+
float32: float32$1,
|
|
831
|
+
float64: float64$1,
|
|
796
832
|
int16: int16$1,
|
|
797
|
-
uint16: uint16$1,
|
|
798
833
|
int32: int32$1,
|
|
799
|
-
uint32: uint32$1,
|
|
800
834
|
int64: int64$1,
|
|
835
|
+
int8: int8$1,
|
|
836
|
+
number: number$1,
|
|
837
|
+
string: string$1,
|
|
838
|
+
uint16: uint16$1,
|
|
839
|
+
uint32: uint32$1,
|
|
801
840
|
uint64: uint64$1,
|
|
802
|
-
|
|
803
|
-
|
|
841
|
+
uint8: uint8$1,
|
|
842
|
+
utf8Length: utf8Length,
|
|
843
|
+
utf8Write: utf8Write,
|
|
804
844
|
writeFloat32: writeFloat32,
|
|
805
|
-
writeFloat64: writeFloat64
|
|
806
|
-
boolean: boolean$1,
|
|
807
|
-
string: string$1,
|
|
808
|
-
number: number$1
|
|
845
|
+
writeFloat64: writeFloat64
|
|
809
846
|
});
|
|
810
847
|
|
|
811
848
|
class EncodeSchemaError extends Error {
|
|
@@ -947,6 +984,18 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
|
|
|
947
984
|
}
|
|
948
985
|
const type = changeTree.getType(field);
|
|
949
986
|
const value = changeTree.getValue(field);
|
|
987
|
+
// try { throw new Error(); } catch (e) {
|
|
988
|
+
// // only print if not coming from Reflection.ts
|
|
989
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
990
|
+
// console.log("encodeKeyValueOperation -> ", {
|
|
991
|
+
// ref: changeTree.ref.constructor.name,
|
|
992
|
+
// field,
|
|
993
|
+
// operation: OPERATION[operation],
|
|
994
|
+
// value: value?.toJSON(),
|
|
995
|
+
// items: ref.toJSON(),
|
|
996
|
+
// });
|
|
997
|
+
// }
|
|
998
|
+
// }
|
|
950
999
|
// TODO: inline this function call small performance gain
|
|
951
1000
|
encodeValue(encoder, bytes, ref, type, value, field, operation, it);
|
|
952
1001
|
};
|
|
@@ -956,15 +1005,19 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
|
|
|
956
1005
|
*/
|
|
957
1006
|
const encodeArray = function (encoder, bytes, changeTree, field, operation, it, isEncodeAll, hasView) {
|
|
958
1007
|
const ref = changeTree.ref;
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1008
|
+
const useOperationByRefId = hasView && changeTree.isFiltered && (typeof (changeTree.getType(field)) !== "string");
|
|
1009
|
+
let refOrIndex;
|
|
1010
|
+
if (useOperationByRefId) {
|
|
1011
|
+
refOrIndex = ref['tmpItems'][field][$changes].refId;
|
|
1012
|
+
if (operation === exports.OPERATION.DELETE) {
|
|
1013
|
+
operation = exports.OPERATION.DELETE_BY_REFID;
|
|
1014
|
+
}
|
|
1015
|
+
else if (operation === exports.OPERATION.ADD) {
|
|
1016
|
+
operation = exports.OPERATION.ADD_BY_REFID;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
else {
|
|
1020
|
+
refOrIndex = field;
|
|
968
1021
|
}
|
|
969
1022
|
// encode operation
|
|
970
1023
|
bytes[it.offset++] = operation & 255;
|
|
@@ -973,7 +1026,7 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
973
1026
|
return;
|
|
974
1027
|
}
|
|
975
1028
|
// encode index
|
|
976
|
-
number$1(bytes,
|
|
1029
|
+
number$1(bytes, refOrIndex, it);
|
|
977
1030
|
// Do not encode value for DELETE operations
|
|
978
1031
|
if (operation === exports.OPERATION.DELETE) {
|
|
979
1032
|
return;
|
|
@@ -1013,9 +1066,9 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
1013
1066
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1014
1067
|
* SOFTWARE
|
|
1015
1068
|
*/
|
|
1016
|
-
function utf8Read(bytes,
|
|
1069
|
+
function utf8Read(bytes, it, length) {
|
|
1017
1070
|
var string = '', chr = 0;
|
|
1018
|
-
for (var i = offset, end = offset + length; i < end; i++) {
|
|
1071
|
+
for (var i = it.offset, end = it.offset + length; i < end; i++) {
|
|
1019
1072
|
var byte = bytes[i];
|
|
1020
1073
|
if ((byte & 0x80) === 0x00) {
|
|
1021
1074
|
string += String.fromCharCode(byte);
|
|
@@ -1050,6 +1103,7 @@ function utf8Read(bytes, offset, length) {
|
|
|
1050
1103
|
// (do not throw error to avoid server/client from crashing due to hack attemps)
|
|
1051
1104
|
// throw new Error('Invalid byte ' + byte.toString(16));
|
|
1052
1105
|
}
|
|
1106
|
+
it.offset += length;
|
|
1053
1107
|
return string;
|
|
1054
1108
|
}
|
|
1055
1109
|
function int8(bytes, it) {
|
|
@@ -1117,9 +1171,7 @@ function string(bytes, it) {
|
|
|
1117
1171
|
else if (prefix === 0xdb) {
|
|
1118
1172
|
length = uint32(bytes, it);
|
|
1119
1173
|
}
|
|
1120
|
-
|
|
1121
|
-
it.offset += length;
|
|
1122
|
-
return value;
|
|
1174
|
+
return utf8Read(bytes, it, length);
|
|
1123
1175
|
}
|
|
1124
1176
|
function stringCheck(bytes, it) {
|
|
1125
1177
|
const prefix = bytes[it.offset];
|
|
@@ -1223,30 +1275,31 @@ function switchStructureCheck(bytes, it) {
|
|
|
1223
1275
|
|
|
1224
1276
|
var decode = /*#__PURE__*/Object.freeze({
|
|
1225
1277
|
__proto__: null,
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
int16: int16,
|
|
1229
|
-
uint16: uint16,
|
|
1230
|
-
int32: int32,
|
|
1231
|
-
uint32: uint32,
|
|
1278
|
+
arrayCheck: arrayCheck,
|
|
1279
|
+
boolean: boolean,
|
|
1232
1280
|
float32: float32,
|
|
1233
1281
|
float64: float64,
|
|
1282
|
+
int16: int16,
|
|
1283
|
+
int32: int32,
|
|
1234
1284
|
int64: int64,
|
|
1235
|
-
|
|
1285
|
+
int8: int8,
|
|
1286
|
+
number: number,
|
|
1287
|
+
numberCheck: numberCheck,
|
|
1236
1288
|
readFloat32: readFloat32,
|
|
1237
1289
|
readFloat64: readFloat64,
|
|
1238
|
-
boolean: boolean,
|
|
1239
1290
|
string: string,
|
|
1240
1291
|
stringCheck: stringCheck,
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1292
|
+
switchStructureCheck: switchStructureCheck,
|
|
1293
|
+
uint16: uint16,
|
|
1294
|
+
uint32: uint32,
|
|
1295
|
+
uint64: uint64,
|
|
1296
|
+
uint8: uint8,
|
|
1297
|
+
utf8Read: utf8Read
|
|
1245
1298
|
});
|
|
1246
1299
|
|
|
1247
1300
|
const DEFINITION_MISMATCH = -1;
|
|
1248
1301
|
function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges) {
|
|
1249
|
-
const $root = decoder
|
|
1302
|
+
const $root = decoder.root;
|
|
1250
1303
|
const previousValue = ref[$getByIndex](index);
|
|
1251
1304
|
let value;
|
|
1252
1305
|
if ((operation & exports.OPERATION.DELETE) === exports.OPERATION.DELETE) {
|
|
@@ -1432,7 +1485,8 @@ const decodeKeyValueOperation = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1432
1485
|
};
|
|
1433
1486
|
const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
1434
1487
|
// "uncompressed" index + operation (array/map items)
|
|
1435
|
-
|
|
1488
|
+
let operation = bytes[it.offset++];
|
|
1489
|
+
let index;
|
|
1436
1490
|
if (operation === exports.OPERATION.CLEAR) {
|
|
1437
1491
|
//
|
|
1438
1492
|
// When decoding:
|
|
@@ -1446,8 +1500,8 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1446
1500
|
else if (operation === exports.OPERATION.DELETE_BY_REFID) {
|
|
1447
1501
|
// TODO: refactor here, try to follow same flow as below
|
|
1448
1502
|
const refId = number(bytes, it);
|
|
1449
|
-
const previousValue = decoder
|
|
1450
|
-
|
|
1503
|
+
const previousValue = decoder.root.refs.get(refId);
|
|
1504
|
+
index = ref.findIndex((value) => value === previousValue);
|
|
1451
1505
|
ref[$deleteByIndex](index);
|
|
1452
1506
|
allChanges.push({
|
|
1453
1507
|
ref,
|
|
@@ -1460,7 +1514,18 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1460
1514
|
});
|
|
1461
1515
|
return;
|
|
1462
1516
|
}
|
|
1463
|
-
|
|
1517
|
+
else if (operation === exports.OPERATION.ADD_BY_REFID) {
|
|
1518
|
+
// operation = OPERATION.ADD;
|
|
1519
|
+
const refId = number(bytes, it);
|
|
1520
|
+
const itemByRefId = decoder.root.refs.get(refId);
|
|
1521
|
+
// use existing index, or push new value
|
|
1522
|
+
index = (itemByRefId)
|
|
1523
|
+
? ref.findIndex((value) => value === itemByRefId)
|
|
1524
|
+
: ref.length;
|
|
1525
|
+
}
|
|
1526
|
+
else {
|
|
1527
|
+
index = number(bytes, it);
|
|
1528
|
+
}
|
|
1464
1529
|
const type = ref[$childType];
|
|
1465
1530
|
let dynamicIndex = index;
|
|
1466
1531
|
const { value, previousValue } = decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges);
|
|
@@ -1869,14 +1934,6 @@ class ArraySchema {
|
|
|
1869
1934
|
lastIndexOf(searchElement, fromIndex = this.length - 1) {
|
|
1870
1935
|
return this.items.lastIndexOf(searchElement, fromIndex);
|
|
1871
1936
|
}
|
|
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
1937
|
every(callbackfn, thisArg) {
|
|
1881
1938
|
return this.items.every(callbackfn, thisArg);
|
|
1882
1939
|
}
|
|
@@ -2555,6 +2612,7 @@ function view(tag = DEFAULT_VIEW_TAG) {
|
|
|
2555
2612
|
const constructor = target.constructor;
|
|
2556
2613
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
2557
2614
|
const parentMetadata = parentClass[Symbol.metadata];
|
|
2615
|
+
// TODO: use Metadata.initialize()
|
|
2558
2616
|
const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
|
|
2559
2617
|
if (!metadata[fieldName]) {
|
|
2560
2618
|
//
|
|
@@ -2579,8 +2637,8 @@ function type(type, options) {
|
|
|
2579
2637
|
// for inheritance support
|
|
2580
2638
|
TypeContext.register(constructor);
|
|
2581
2639
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
2582
|
-
const parentMetadata = parentClass[Symbol.metadata];
|
|
2583
|
-
const metadata =
|
|
2640
|
+
const parentMetadata = parentClass && parentClass[Symbol.metadata];
|
|
2641
|
+
const metadata = Metadata.initialize(constructor, parentMetadata);
|
|
2584
2642
|
let fieldIndex;
|
|
2585
2643
|
/**
|
|
2586
2644
|
* skip if descriptor already exists for this field (`@deprecated()`)
|
|
@@ -3353,6 +3411,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
3353
3411
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
3354
3412
|
PERFORMANCE OF THIS SOFTWARE.
|
|
3355
3413
|
***************************************************************************** */
|
|
3414
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
3415
|
+
|
|
3356
3416
|
|
|
3357
3417
|
function __decorate(decorators, target, key, desc) {
|
|
3358
3418
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -3384,11 +3444,14 @@ class Encoder {
|
|
|
3384
3444
|
setRoot(state) {
|
|
3385
3445
|
this.root = new Root();
|
|
3386
3446
|
this.state = state;
|
|
3447
|
+
// Workaround to allow using an empty Schema.
|
|
3448
|
+
if (state.constructor[Symbol.metadata] === undefined) {
|
|
3449
|
+
Metadata.init(state);
|
|
3450
|
+
}
|
|
3387
3451
|
state[$changes].setRoot(this.root);
|
|
3388
3452
|
}
|
|
3389
|
-
encode(it = { offset: 0 }, view,
|
|
3453
|
+
encode(it = { offset: 0 }, view, buffer = this.sharedBuffer, changeTrees = this.root.changes, isEncodeAll = this.root.allChanges === changeTrees) {
|
|
3390
3454
|
const initialOffset = it.offset; // cache current offset in case we need to resize the buffer
|
|
3391
|
-
const isEncodeAll = this.root.allChanges === changeTrees;
|
|
3392
3455
|
const hasView = (view !== undefined);
|
|
3393
3456
|
const rootChangeTree = this.state[$changes];
|
|
3394
3457
|
const changeTreesIterator = changeTrees.entries();
|
|
@@ -3397,6 +3460,12 @@ class Encoder {
|
|
|
3397
3460
|
const ctor = ref['constructor'];
|
|
3398
3461
|
const encoder = ctor[$encoder];
|
|
3399
3462
|
const filter = ctor[$filter];
|
|
3463
|
+
// try { throw new Error(); } catch (e) {
|
|
3464
|
+
// // only print if not coming from Reflection.ts
|
|
3465
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
3466
|
+
// console.log("ChangeTree:", { ref: ref.constructor.name, });
|
|
3467
|
+
// }
|
|
3468
|
+
// }
|
|
3400
3469
|
if (hasView) {
|
|
3401
3470
|
if (!view.items.has(changeTree)) {
|
|
3402
3471
|
view.invisible.add(changeTree);
|
|
@@ -3408,8 +3477,8 @@ class Encoder {
|
|
|
3408
3477
|
}
|
|
3409
3478
|
// skip root `refId` if it's the first change tree
|
|
3410
3479
|
if (it.offset !== initialOffset || changeTree !== rootChangeTree) {
|
|
3411
|
-
|
|
3412
|
-
number$1(
|
|
3480
|
+
buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
|
|
3481
|
+
number$1(buffer, changeTree.refId, it);
|
|
3413
3482
|
}
|
|
3414
3483
|
const changesIterator = changes.entries();
|
|
3415
3484
|
for (const [fieldIndex, operation] of changesIterator) {
|
|
@@ -3426,22 +3495,31 @@ class Encoder {
|
|
|
3426
3495
|
// view?.invisible.add(changeTree);
|
|
3427
3496
|
continue;
|
|
3428
3497
|
}
|
|
3429
|
-
//
|
|
3430
|
-
//
|
|
3431
|
-
//
|
|
3432
|
-
//
|
|
3433
|
-
//
|
|
3434
|
-
|
|
3498
|
+
// try { throw new Error(); } catch (e) {
|
|
3499
|
+
// // only print if not coming from Reflection.ts
|
|
3500
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
3501
|
+
// console.log("WILL ENCODE", {
|
|
3502
|
+
// ref: changeTree.ref.constructor.name,
|
|
3503
|
+
// fieldIndex,
|
|
3504
|
+
// operation: OPERATION[operation],
|
|
3505
|
+
// });
|
|
3506
|
+
// }
|
|
3507
|
+
// }
|
|
3508
|
+
encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
|
|
3435
3509
|
}
|
|
3436
3510
|
}
|
|
3437
|
-
if (it.offset >
|
|
3438
|
-
const newSize = getNextPowerOf2(
|
|
3439
|
-
console.warn("@colyseus/schema encode buffer overflow. Current buffer size: " +
|
|
3511
|
+
if (it.offset > buffer.byteLength) {
|
|
3512
|
+
const newSize = getNextPowerOf2(buffer.byteLength * 2);
|
|
3513
|
+
console.warn("@colyseus/schema encode buffer overflow. Current buffer size: " + buffer.byteLength + ", encoding offset: " + it.offset + ", new size: " + newSize);
|
|
3440
3514
|
//
|
|
3441
3515
|
// resize buffer and re-encode (TODO: can we avoid re-encoding here?)
|
|
3442
3516
|
//
|
|
3443
|
-
|
|
3444
|
-
|
|
3517
|
+
buffer = Buffer.allocUnsafeSlow(newSize);
|
|
3518
|
+
// assign resized buffer to local sharedBuffer
|
|
3519
|
+
if (buffer === this.sharedBuffer) {
|
|
3520
|
+
this.sharedBuffer = buffer;
|
|
3521
|
+
}
|
|
3522
|
+
return this.encode({ offset: initialOffset }, view, buffer, changeTrees, isEncodeAll);
|
|
3445
3523
|
}
|
|
3446
3524
|
else {
|
|
3447
3525
|
//
|
|
@@ -3453,38 +3531,37 @@ class Encoder {
|
|
|
3453
3531
|
//
|
|
3454
3532
|
this.onEndEncode(changeTrees);
|
|
3455
3533
|
}
|
|
3456
|
-
|
|
3457
|
-
return bytes.slice(0, it.offset);
|
|
3534
|
+
return buffer.subarray(0, it.offset);
|
|
3458
3535
|
}
|
|
3459
3536
|
}
|
|
3460
|
-
encodeAll(it = { offset: 0 }) {
|
|
3461
|
-
// console.log(`encodeAll(), this
|
|
3462
|
-
// Array.from(this
|
|
3463
|
-
// console.log("->", item[0].
|
|
3537
|
+
encodeAll(it = { offset: 0 }, buffer = this.sharedBuffer) {
|
|
3538
|
+
// console.log(`encodeAll(), this.root.allChanges (${this.root.allChanges.size})`);
|
|
3539
|
+
// Array.from(this.root.allChanges.entries()).map((item) => {
|
|
3540
|
+
// console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
|
|
3464
3541
|
// });
|
|
3465
|
-
return this.encode(it, undefined,
|
|
3542
|
+
return this.encode(it, undefined, buffer, this.root.allChanges, true);
|
|
3466
3543
|
}
|
|
3467
3544
|
encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3468
3545
|
const viewOffset = it.offset;
|
|
3469
|
-
// console.log(`encodeAllView(), this
|
|
3546
|
+
// console.log(`encodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
|
|
3470
3547
|
// this.debugAllFilteredChanges();
|
|
3471
3548
|
// try to encode "filtered" changes
|
|
3472
|
-
this.encode(it, view, bytes, this.root.allFilteredChanges);
|
|
3549
|
+
this.encode(it, view, bytes, this.root.allFilteredChanges, true);
|
|
3473
3550
|
return Buffer.concat([
|
|
3474
|
-
bytes.
|
|
3475
|
-
bytes.
|
|
3551
|
+
bytes.subarray(0, sharedOffset),
|
|
3552
|
+
bytes.subarray(viewOffset, it.offset)
|
|
3476
3553
|
]);
|
|
3477
3554
|
}
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3555
|
+
debugAllFilteredChanges() {
|
|
3556
|
+
Array.from(this.root.allFilteredChanges.entries()).map((item) => {
|
|
3557
|
+
console.log("->", { refId: item[0].refId, changes: item[1].size }, item[0].ref.toJSON());
|
|
3558
|
+
if (Array.isArray(item[0].ref.toJSON())) {
|
|
3559
|
+
item[1].forEach((op, key) => {
|
|
3560
|
+
console.log(" ->", { key, op: exports.OPERATION[op] });
|
|
3561
|
+
});
|
|
3562
|
+
}
|
|
3563
|
+
});
|
|
3564
|
+
}
|
|
3488
3565
|
encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3489
3566
|
const viewOffset = it.offset;
|
|
3490
3567
|
// try to encode "filtered" changes
|
|
@@ -3516,8 +3593,8 @@ class Encoder {
|
|
|
3516
3593
|
// clear "view" changes after encoding
|
|
3517
3594
|
view.changes.clear();
|
|
3518
3595
|
return Buffer.concat([
|
|
3519
|
-
bytes.
|
|
3520
|
-
bytes.
|
|
3596
|
+
bytes.subarray(0, sharedOffset),
|
|
3597
|
+
bytes.subarray(viewOffset, it.offset)
|
|
3521
3598
|
]);
|
|
3522
3599
|
}
|
|
3523
3600
|
onEndEncode(changeTrees = this.root.changes) {
|
|
@@ -3696,12 +3773,12 @@ class Decoder {
|
|
|
3696
3773
|
}
|
|
3697
3774
|
setRoot(root) {
|
|
3698
3775
|
this.state = root;
|
|
3699
|
-
this
|
|
3700
|
-
this
|
|
3776
|
+
this.root = new ReferenceTracker();
|
|
3777
|
+
this.root.addRef(0, root);
|
|
3701
3778
|
}
|
|
3702
3779
|
decode(bytes, it = { offset: 0 }, ref = this.state) {
|
|
3703
3780
|
const allChanges = [];
|
|
3704
|
-
const $root = this
|
|
3781
|
+
const $root = this.root;
|
|
3705
3782
|
const totalBytes = bytes.byteLength;
|
|
3706
3783
|
let decoder = ref['constructor'][$decoder];
|
|
3707
3784
|
this.currentRefId = 0;
|
|
@@ -3782,7 +3859,7 @@ class Decoder {
|
|
|
3782
3859
|
previousValue: value
|
|
3783
3860
|
});
|
|
3784
3861
|
if (needRemoveRef) {
|
|
3785
|
-
this
|
|
3862
|
+
this.root.removeRef(this.root.refIds.get(value));
|
|
3786
3863
|
}
|
|
3787
3864
|
});
|
|
3788
3865
|
}
|
|
@@ -3822,7 +3899,7 @@ class Reflection extends Schema {
|
|
|
3822
3899
|
super(...arguments);
|
|
3823
3900
|
this.types = new ArraySchema();
|
|
3824
3901
|
}
|
|
3825
|
-
static encode(instance, context) {
|
|
3902
|
+
static encode(instance, context, it = { offset: 0 }) {
|
|
3826
3903
|
if (!context) {
|
|
3827
3904
|
context = new TypeContext(instance.constructor);
|
|
3828
3905
|
}
|
|
@@ -3879,7 +3956,6 @@ class Reflection extends Schema {
|
|
|
3879
3956
|
}
|
|
3880
3957
|
buildType(type, klass[Symbol.metadata]);
|
|
3881
3958
|
}
|
|
3882
|
-
const it = { offset: 0 };
|
|
3883
3959
|
const buf = encoder.encodeAll(it);
|
|
3884
3960
|
return Buffer.from(buf, 0, it.offset);
|
|
3885
3961
|
}
|
|
@@ -3887,59 +3963,298 @@ class Reflection extends Schema {
|
|
|
3887
3963
|
const reflection = new Reflection();
|
|
3888
3964
|
const reflectionDecoder = new Decoder(reflection);
|
|
3889
3965
|
reflectionDecoder.decode(bytes, it);
|
|
3890
|
-
const
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
const
|
|
3966
|
+
const typeContext = new TypeContext();
|
|
3967
|
+
// 1st pass, initialize metadata + inheritance
|
|
3968
|
+
reflection.types.forEach((reflectionType) => {
|
|
3969
|
+
const parentClass = typeContext.get(reflectionType.extendsId) ?? Schema;
|
|
3970
|
+
const schema = class _ extends parentClass {
|
|
3894
3971
|
};
|
|
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 });
|
|
3972
|
+
const parentMetadata = parentClass[Symbol.metadata];
|
|
3898
3973
|
// register for inheritance support
|
|
3899
3974
|
TypeContext.register(schema);
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
return types;
|
|
3975
|
+
// for inheritance support
|
|
3976
|
+
Metadata.initialize(schema, parentMetadata);
|
|
3977
|
+
typeContext.add(schema, reflectionType.id);
|
|
3904
3978
|
}, {});
|
|
3979
|
+
// 2nd pass, set fields
|
|
3905
3980
|
reflection.types.forEach((reflectionType) => {
|
|
3906
|
-
const schemaType =
|
|
3981
|
+
const schemaType = typeContext.get(reflectionType.id);
|
|
3907
3982
|
const metadata = schemaType[Symbol.metadata];
|
|
3908
|
-
|
|
3909
|
-
const parentFieldIndex =
|
|
3983
|
+
// FIXME: use metadata[-1] to get field count
|
|
3984
|
+
const parentFieldIndex = 0;
|
|
3985
|
+
// console.log("--------------------");
|
|
3986
|
+
// // console.log("reflectionType", reflectionType.toJSON());
|
|
3987
|
+
// console.log("reflectionType.fields", reflectionType.fields.toJSON());
|
|
3988
|
+
// console.log("parentFieldIndex", parentFieldIndex);
|
|
3989
|
+
//
|
|
3990
|
+
// FIXME: set fields using parentKlass as well
|
|
3991
|
+
// currently the fields are duplicated on inherited classes
|
|
3992
|
+
//
|
|
3993
|
+
// // const parentKlass = reflection.types[reflectionType.extendsId];
|
|
3994
|
+
// // parentKlass.fields
|
|
3910
3995
|
reflectionType.fields.forEach((field, i) => {
|
|
3911
3996
|
const fieldIndex = parentFieldIndex + i;
|
|
3912
3997
|
if (field.referencedType !== undefined) {
|
|
3913
3998
|
let fieldType = field.type;
|
|
3914
|
-
let refType =
|
|
3999
|
+
let refType = typeContext.get(field.referencedType);
|
|
3915
4000
|
// map or array of primitive type (-1)
|
|
3916
4001
|
if (!refType) {
|
|
3917
4002
|
const typeInfo = field.type.split(":");
|
|
3918
4003
|
fieldType = typeInfo[0];
|
|
3919
|
-
refType = typeInfo[1];
|
|
4004
|
+
refType = typeInfo[1]; // string
|
|
3920
4005
|
}
|
|
3921
4006
|
if (fieldType === "ref") {
|
|
3922
|
-
// type(refType)(schemaType.prototype, field.name);
|
|
3923
4007
|
Metadata.addField(metadata, fieldIndex, field.name, refType);
|
|
3924
4008
|
}
|
|
3925
4009
|
else {
|
|
3926
|
-
// type({ [fieldType]: refType } as DefinitionType)(schemaType.prototype, field.name);
|
|
3927
4010
|
Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType });
|
|
3928
4011
|
}
|
|
3929
4012
|
}
|
|
3930
4013
|
else {
|
|
3931
|
-
// type(field.type as PrimitiveType)(schemaType.prototype, field.name);
|
|
3932
4014
|
Metadata.addField(metadata, fieldIndex, field.name, field.type);
|
|
3933
4015
|
}
|
|
3934
4016
|
});
|
|
3935
4017
|
});
|
|
3936
|
-
|
|
4018
|
+
// @ts-ignore
|
|
4019
|
+
return new (typeContext.get(0))();
|
|
3937
4020
|
}
|
|
3938
4021
|
}
|
|
3939
4022
|
__decorate([
|
|
3940
4023
|
type([ReflectionType])
|
|
3941
4024
|
], Reflection.prototype, "types", void 0);
|
|
3942
4025
|
|
|
4026
|
+
function getDecoderStateCallbacks(decoder) {
|
|
4027
|
+
const $root = decoder.root;
|
|
4028
|
+
const callbacks = $root.callbacks;
|
|
4029
|
+
let isTriggeringOnAdd = false;
|
|
4030
|
+
decoder.triggerChanges = function (allChanges) {
|
|
4031
|
+
const uniqueRefIds = new Set();
|
|
4032
|
+
for (let i = 0, l = allChanges.length; i < l; i++) {
|
|
4033
|
+
const change = allChanges[i];
|
|
4034
|
+
const refId = change.refId;
|
|
4035
|
+
const ref = change.ref;
|
|
4036
|
+
const $callbacks = callbacks[refId];
|
|
4037
|
+
if (!$callbacks) {
|
|
4038
|
+
continue;
|
|
4039
|
+
}
|
|
4040
|
+
//
|
|
4041
|
+
// trigger onRemove on child structure.
|
|
4042
|
+
//
|
|
4043
|
+
if ((change.op & exports.OPERATION.DELETE) === exports.OPERATION.DELETE &&
|
|
4044
|
+
change.previousValue instanceof Schema) {
|
|
4045
|
+
const deleteCallbacks = callbacks[$root.refIds.get(change.previousValue)]?.[exports.OPERATION.DELETE];
|
|
4046
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
4047
|
+
deleteCallbacks[i]();
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
if (ref instanceof Schema) {
|
|
4051
|
+
//
|
|
4052
|
+
// Handle schema instance
|
|
4053
|
+
//
|
|
4054
|
+
if (!uniqueRefIds.has(refId)) {
|
|
4055
|
+
// trigger onChange
|
|
4056
|
+
const replaceCallbacks = $callbacks?.[exports.OPERATION.REPLACE];
|
|
4057
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
4058
|
+
replaceCallbacks[i]();
|
|
4059
|
+
// try {
|
|
4060
|
+
// } catch (e) {
|
|
4061
|
+
// console.error(e);
|
|
4062
|
+
// }
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
4065
|
+
if ($callbacks.hasOwnProperty(change.field)) {
|
|
4066
|
+
const fieldCallbacks = $callbacks[change.field];
|
|
4067
|
+
for (let i = fieldCallbacks?.length - 1; i >= 0; i--) {
|
|
4068
|
+
fieldCallbacks[i](change.value, change.previousValue);
|
|
4069
|
+
// try {
|
|
4070
|
+
// } catch (e) {
|
|
4071
|
+
// console.error(e);
|
|
4072
|
+
// }
|
|
4073
|
+
}
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
else {
|
|
4077
|
+
//
|
|
4078
|
+
// Handle collection of items
|
|
4079
|
+
//
|
|
4080
|
+
if ((change.op & exports.OPERATION.DELETE) === exports.OPERATION.DELETE) {
|
|
4081
|
+
//
|
|
4082
|
+
// FIXME: `previousValue` should always be available.
|
|
4083
|
+
//
|
|
4084
|
+
if (change.previousValue !== undefined) {
|
|
4085
|
+
// triger onRemove
|
|
4086
|
+
const deleteCallbacks = $callbacks[exports.OPERATION.DELETE];
|
|
4087
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
4088
|
+
deleteCallbacks[i](change.previousValue, change.dynamicIndex ?? change.field);
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
// Handle DELETE_AND_ADD operations
|
|
4092
|
+
// FIXME: should we set "isTriggeringOnAdd" here?
|
|
4093
|
+
if ((change.op & exports.OPERATION.ADD) === exports.OPERATION.ADD) {
|
|
4094
|
+
const addCallbacks = $callbacks[exports.OPERATION.ADD];
|
|
4095
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4096
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
else if ((change.op & exports.OPERATION.ADD) === exports.OPERATION.ADD && change.previousValue === undefined) {
|
|
4101
|
+
// triger onAdd
|
|
4102
|
+
isTriggeringOnAdd = true;
|
|
4103
|
+
const addCallbacks = $callbacks[exports.OPERATION.ADD];
|
|
4104
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4105
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4106
|
+
}
|
|
4107
|
+
isTriggeringOnAdd = false;
|
|
4108
|
+
}
|
|
4109
|
+
// trigger onChange
|
|
4110
|
+
if (change.value !== change.previousValue) {
|
|
4111
|
+
const replaceCallbacks = $callbacks[exports.OPERATION.REPLACE];
|
|
4112
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
4113
|
+
replaceCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4114
|
+
}
|
|
4115
|
+
}
|
|
4116
|
+
}
|
|
4117
|
+
uniqueRefIds.add(refId);
|
|
4118
|
+
}
|
|
4119
|
+
};
|
|
4120
|
+
function getProxy(metadataOrType, context) {
|
|
4121
|
+
let metadata = context.instance?.constructor[Symbol.metadata] || metadataOrType;
|
|
4122
|
+
let isCollection = ((context.instance && typeof (context.instance['forEach']) === "function") ||
|
|
4123
|
+
(metadataOrType && typeof (metadataOrType[Symbol.metadata]) === "undefined"));
|
|
4124
|
+
if (metadata && !isCollection) {
|
|
4125
|
+
const onAdd = function (ref, prop, callback, immediate) {
|
|
4126
|
+
// immediate trigger
|
|
4127
|
+
if (immediate &&
|
|
4128
|
+
context.instance[prop] !== undefined &&
|
|
4129
|
+
!isTriggeringOnAdd // FIXME: This is a workaround (https://github.com/colyseus/schema/issues/147)
|
|
4130
|
+
) {
|
|
4131
|
+
callback(context.instance[prop], undefined);
|
|
4132
|
+
}
|
|
4133
|
+
return $root.addCallback($root.refIds.get(ref), prop, callback);
|
|
4134
|
+
};
|
|
4135
|
+
/**
|
|
4136
|
+
* Schema instances
|
|
4137
|
+
*/
|
|
4138
|
+
return new Proxy({
|
|
4139
|
+
listen: function listen(prop, callback, immediate = true) {
|
|
4140
|
+
if (context.instance) {
|
|
4141
|
+
return onAdd(context.instance, prop, callback, immediate);
|
|
4142
|
+
}
|
|
4143
|
+
else {
|
|
4144
|
+
// collection instance not received yet
|
|
4145
|
+
context.onInstanceAvailable((ref, existing) => onAdd(ref, prop, callback, immediate && existing));
|
|
4146
|
+
}
|
|
4147
|
+
},
|
|
4148
|
+
onChange: function onChange(callback) {
|
|
4149
|
+
return $root.addCallback($root.refIds.get(context.instance), exports.OPERATION.REPLACE, callback);
|
|
4150
|
+
},
|
|
4151
|
+
bindTo: function bindTo(targetObject, properties) {
|
|
4152
|
+
//
|
|
4153
|
+
// TODO: refactor this implementation. There is room for improvement here.
|
|
4154
|
+
//
|
|
4155
|
+
if (!properties) {
|
|
4156
|
+
properties = Object.keys(metadata);
|
|
4157
|
+
}
|
|
4158
|
+
return $root.addCallback($root.refIds.get(context.instance), exports.OPERATION.REPLACE, () => {
|
|
4159
|
+
properties.forEach((prop) => targetObject[prop] = context.instance[prop]);
|
|
4160
|
+
});
|
|
4161
|
+
}
|
|
4162
|
+
}, {
|
|
4163
|
+
get(target, prop) {
|
|
4164
|
+
if (metadata[prop]) {
|
|
4165
|
+
const instance = context.instance?.[prop];
|
|
4166
|
+
const onInstanceAvailable = ((callback) => {
|
|
4167
|
+
const unbind = $(context.instance).listen(prop, (value, _) => {
|
|
4168
|
+
callback(value, false);
|
|
4169
|
+
// FIXME: by "unbinding" the callback here,
|
|
4170
|
+
// it will not support when the server
|
|
4171
|
+
// re-instantiates the instance.
|
|
4172
|
+
//
|
|
4173
|
+
unbind?.();
|
|
4174
|
+
}, false);
|
|
4175
|
+
// has existing value
|
|
4176
|
+
if ($root.refIds.get(instance) !== undefined) {
|
|
4177
|
+
callback(instance, true);
|
|
4178
|
+
}
|
|
4179
|
+
});
|
|
4180
|
+
return getProxy(metadata[prop].type, {
|
|
4181
|
+
instance,
|
|
4182
|
+
parentInstance: context.instance,
|
|
4183
|
+
onInstanceAvailable,
|
|
4184
|
+
});
|
|
4185
|
+
}
|
|
4186
|
+
else {
|
|
4187
|
+
// accessing the function
|
|
4188
|
+
return target[prop];
|
|
4189
|
+
}
|
|
4190
|
+
},
|
|
4191
|
+
has(target, prop) { return metadata[prop] !== undefined; },
|
|
4192
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4193
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4194
|
+
});
|
|
4195
|
+
}
|
|
4196
|
+
else {
|
|
4197
|
+
/**
|
|
4198
|
+
* Collection instances
|
|
4199
|
+
*/
|
|
4200
|
+
const onAdd = function (ref, callback, immediate) {
|
|
4201
|
+
// Trigger callback on existing items
|
|
4202
|
+
if (immediate) {
|
|
4203
|
+
ref.forEach((v, k) => callback(v, k));
|
|
4204
|
+
}
|
|
4205
|
+
return $root.addCallback($root.refIds.get(ref), exports.OPERATION.ADD, callback);
|
|
4206
|
+
};
|
|
4207
|
+
const onRemove = function (ref, callback) {
|
|
4208
|
+
return $root.addCallback($root.refIds.get(ref), exports.OPERATION.DELETE, callback);
|
|
4209
|
+
};
|
|
4210
|
+
return new Proxy({
|
|
4211
|
+
onAdd: function (callback, immediate = true) {
|
|
4212
|
+
//
|
|
4213
|
+
// https://github.com/colyseus/schema/issues/147
|
|
4214
|
+
// If parent instance has "onAdd" registered, avoid triggering immediate callback.
|
|
4215
|
+
//
|
|
4216
|
+
// FIXME: "isTriggeringOnAdd" is a workaround. We should find a better way to handle this.
|
|
4217
|
+
//
|
|
4218
|
+
if (context.onInstanceAvailable) {
|
|
4219
|
+
// collection instance not received yet
|
|
4220
|
+
context.onInstanceAvailable((ref, existing) => onAdd(ref, callback, immediate && existing && !isTriggeringOnAdd));
|
|
4221
|
+
}
|
|
4222
|
+
else if (context.instance) {
|
|
4223
|
+
onAdd(context.instance, callback, immediate && !isTriggeringOnAdd);
|
|
4224
|
+
}
|
|
4225
|
+
},
|
|
4226
|
+
onRemove: function (callback) {
|
|
4227
|
+
if (context.onInstanceAvailable) {
|
|
4228
|
+
// collection instance not received yet
|
|
4229
|
+
context.onInstanceAvailable((ref) => onRemove(ref, callback));
|
|
4230
|
+
}
|
|
4231
|
+
else if (context.instance) {
|
|
4232
|
+
onRemove(context.instance, callback);
|
|
4233
|
+
}
|
|
4234
|
+
},
|
|
4235
|
+
}, {
|
|
4236
|
+
get(target, prop) {
|
|
4237
|
+
if (!target[prop]) {
|
|
4238
|
+
throw new Error(`Can't access '${prop}' through callback proxy. access the instance directly.`);
|
|
4239
|
+
}
|
|
4240
|
+
return target[prop];
|
|
4241
|
+
},
|
|
4242
|
+
has(target, prop) { return target[prop] !== undefined; },
|
|
4243
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4244
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4245
|
+
});
|
|
4246
|
+
}
|
|
4247
|
+
}
|
|
4248
|
+
function $(instance) {
|
|
4249
|
+
return getProxy(undefined, { instance });
|
|
4250
|
+
}
|
|
4251
|
+
return $;
|
|
4252
|
+
}
|
|
4253
|
+
|
|
4254
|
+
function getRawChangesCallback(decoder, callback) {
|
|
4255
|
+
decoder.triggerChanges = callback;
|
|
4256
|
+
}
|
|
4257
|
+
|
|
3943
4258
|
class StateView {
|
|
3944
4259
|
constructor() {
|
|
3945
4260
|
/**
|
|
@@ -3962,12 +4277,18 @@ class StateView {
|
|
|
3962
4277
|
console.warn("StateView#add(), invalid object:", obj);
|
|
3963
4278
|
return this;
|
|
3964
4279
|
}
|
|
4280
|
+
// FIXME: ArraySchema/MapSchema does not have metadata
|
|
4281
|
+
const metadata = obj.constructor[Symbol.metadata];
|
|
3965
4282
|
let changeTree = obj[$changes];
|
|
3966
4283
|
this.items.add(changeTree);
|
|
3967
4284
|
// Add children of this ChangeTree to this view
|
|
3968
|
-
changeTree.forEachChild((change,
|
|
3969
|
-
|
|
3970
|
-
|
|
4285
|
+
changeTree.forEachChild((change, index) => {
|
|
4286
|
+
// Do not ADD children that don't have the same tag
|
|
4287
|
+
if (metadata && metadata[metadata[index]].tag !== tag) {
|
|
4288
|
+
return;
|
|
4289
|
+
}
|
|
4290
|
+
this.add(change.ref, tag);
|
|
4291
|
+
});
|
|
3971
4292
|
// add parent ChangeTree's, if they are invisible to this view
|
|
3972
4293
|
// TODO: REFACTOR addParent()
|
|
3973
4294
|
this.addParent(changeTree, tag);
|
|
@@ -3994,7 +4315,6 @@ class StateView {
|
|
|
3994
4315
|
tags = this.tags.get(changeTree);
|
|
3995
4316
|
}
|
|
3996
4317
|
tags.add(tag);
|
|
3997
|
-
// console.log("BY TAG:", tag);
|
|
3998
4318
|
// Ref: add tagged properties
|
|
3999
4319
|
metadata?.[-3]?.[tag]?.forEach((index) => {
|
|
4000
4320
|
if (changeTree.getChange(index) !== exports.OPERATION.DELETE) {
|
|
@@ -4164,6 +4484,8 @@ exports.dumpChanges = dumpChanges;
|
|
|
4164
4484
|
exports.encode = encode;
|
|
4165
4485
|
exports.encodeKeyValueOperation = encodeArray;
|
|
4166
4486
|
exports.encodeSchemaOperation = encodeSchemaOperation;
|
|
4487
|
+
exports.getDecoderStateCallbacks = getDecoderStateCallbacks;
|
|
4488
|
+
exports.getRawChangesCallback = getRawChangesCallback;
|
|
4167
4489
|
exports.registerType = registerType;
|
|
4168
4490
|
exports.type = type;
|
|
4169
4491
|
exports.view = view;
|