@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/esm/index.mjs
CHANGED
|
@@ -23,6 +23,7 @@ var OPERATION;
|
|
|
23
23
|
OPERATION[OPERATION["REVERSE"] = 15] = "REVERSE";
|
|
24
24
|
OPERATION[OPERATION["MOVE"] = 32] = "MOVE";
|
|
25
25
|
OPERATION[OPERATION["DELETE_BY_REFID"] = 33] = "DELETE_BY_REFID";
|
|
26
|
+
OPERATION[OPERATION["ADD_BY_REFID"] = 129] = "ADD_BY_REFID";
|
|
26
27
|
})(OPERATION || (OPERATION = {}));
|
|
27
28
|
|
|
28
29
|
Symbol.metadata ??= Symbol.for("Symbol.metadata");
|
|
@@ -139,6 +140,45 @@ const Metadata = {
|
|
|
139
140
|
isDeprecated(metadata, field) {
|
|
140
141
|
return metadata[field].deprecated === true;
|
|
141
142
|
},
|
|
143
|
+
init(klass) {
|
|
144
|
+
//
|
|
145
|
+
// Used only to initialize an empty Schema (Encoder#constructor)
|
|
146
|
+
// TODO: remove/refactor this...
|
|
147
|
+
//
|
|
148
|
+
const metadata = {};
|
|
149
|
+
klass.constructor[Symbol.metadata] = metadata;
|
|
150
|
+
Object.defineProperty(metadata, -1, {
|
|
151
|
+
value: 0,
|
|
152
|
+
enumerable: false,
|
|
153
|
+
configurable: true,
|
|
154
|
+
});
|
|
155
|
+
},
|
|
156
|
+
initialize(constructor, parentMetadata) {
|
|
157
|
+
let metadata = constructor[Symbol.metadata] ?? Object.create(null);
|
|
158
|
+
// make sure inherited classes have their own metadata object.
|
|
159
|
+
if (constructor[Symbol.metadata] === parentMetadata) {
|
|
160
|
+
metadata = Object.create(null);
|
|
161
|
+
if (parentMetadata) {
|
|
162
|
+
// assign parent metadata to current
|
|
163
|
+
Object.assign(metadata, parentMetadata);
|
|
164
|
+
for (let i = 0; i <= parentMetadata[-1]; i++) {
|
|
165
|
+
Object.defineProperty(metadata, i, {
|
|
166
|
+
value: parentMetadata[i],
|
|
167
|
+
enumerable: false,
|
|
168
|
+
configurable: true,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
Object.defineProperty(metadata, -1, {
|
|
172
|
+
value: parentMetadata[-1],
|
|
173
|
+
enumerable: false,
|
|
174
|
+
configurable: true,
|
|
175
|
+
writable: true,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
constructor[Symbol.metadata] = metadata;
|
|
180
|
+
return metadata;
|
|
181
|
+
},
|
|
142
182
|
isValidInstance(klass) {
|
|
143
183
|
return (klass.constructor[Symbol.metadata] &&
|
|
144
184
|
Object.prototype.hasOwnProperty.call(klass.constructor[Symbol.metadata], -1));
|
|
@@ -258,14 +298,12 @@ class ChangeTree {
|
|
|
258
298
|
this.checkIsFiltered(parent, parentIndex);
|
|
259
299
|
if (!this.isFiltered) {
|
|
260
300
|
this.root.changes.set(this, this.changes);
|
|
301
|
+
this.root.allChanges.set(this, this.allChanges);
|
|
261
302
|
}
|
|
262
303
|
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
263
304
|
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
264
305
|
this.root.allFilteredChanges.set(this, this.filteredChanges);
|
|
265
306
|
}
|
|
266
|
-
else {
|
|
267
|
-
this.root.allChanges.set(this, this.allChanges);
|
|
268
|
-
}
|
|
269
307
|
this.ensureRefId();
|
|
270
308
|
this.forEachChild((changeTree, atIndex) => {
|
|
271
309
|
changeTree.setParent(this.ref, root, atIndex);
|
|
@@ -357,7 +395,6 @@ class ChangeTree {
|
|
|
357
395
|
}
|
|
358
396
|
_shiftAllChangeIndexes(shiftIndex, startIndex = 0, allChangeSet) {
|
|
359
397
|
Array.from(allChangeSet.entries()).forEach(([index, op]) => {
|
|
360
|
-
// console.log('shiftAllChangeIndexes', index >= startIndex, { index, op, shiftIndex, startIndex })
|
|
361
398
|
if (index >= startIndex) {
|
|
362
399
|
allChangeSet.delete(index);
|
|
363
400
|
allChangeSet.set(index + shiftIndex, op);
|
|
@@ -498,7 +535,7 @@ class ChangeTree {
|
|
|
498
535
|
}
|
|
499
536
|
checkIsFiltered(parent, parentIndex) {
|
|
500
537
|
// Detect if current structure has "filters" declared
|
|
501
|
-
this.isPartiallyFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-2];
|
|
538
|
+
this.isPartiallyFiltered = (this.ref['constructor']?.[Symbol.metadata]?.[-2] !== undefined);
|
|
502
539
|
// TODO: support "partially filtered", where the instance is visible, but only a field is not.
|
|
503
540
|
// Detect if parent has "filters" declared
|
|
504
541
|
while (parent && !this.isFiltered) {
|
|
@@ -559,26 +596,29 @@ try {
|
|
|
559
596
|
textEncoder = new TextEncoder();
|
|
560
597
|
}
|
|
561
598
|
catch (e) { }
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
599
|
+
const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength);
|
|
600
|
+
const utf8Length = (hasBufferByteLength)
|
|
601
|
+
? Buffer.byteLength // node
|
|
602
|
+
: function (str, _) {
|
|
603
|
+
var c = 0, length = 0;
|
|
604
|
+
for (var i = 0, l = str.length; i < l; i++) {
|
|
605
|
+
c = str.charCodeAt(i);
|
|
606
|
+
if (c < 0x80) {
|
|
607
|
+
length += 1;
|
|
608
|
+
}
|
|
609
|
+
else if (c < 0x800) {
|
|
610
|
+
length += 2;
|
|
611
|
+
}
|
|
612
|
+
else if (c < 0xd800 || c >= 0xe000) {
|
|
613
|
+
length += 3;
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
i++;
|
|
617
|
+
length += 4;
|
|
618
|
+
}
|
|
578
619
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}
|
|
620
|
+
return length;
|
|
621
|
+
};
|
|
582
622
|
function utf8Write(view, str, it) {
|
|
583
623
|
var c = 0;
|
|
584
624
|
for (var i = 0, l = str.length; i < l; i++) {
|
|
@@ -673,8 +713,7 @@ function string$1(bytes, value, it) {
|
|
|
673
713
|
if (!value) {
|
|
674
714
|
value = "";
|
|
675
715
|
}
|
|
676
|
-
|
|
677
|
-
let length = Buffer.byteLength(value, "utf8");
|
|
716
|
+
let length = utf8Length(value, "utf8");
|
|
678
717
|
let size = 0;
|
|
679
718
|
// fixstr
|
|
680
719
|
if (length < 0x20) {
|
|
@@ -785,23 +824,23 @@ function number$1(bytes, value, it) {
|
|
|
785
824
|
|
|
786
825
|
var encode = /*#__PURE__*/Object.freeze({
|
|
787
826
|
__proto__: null,
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
uint8: uint8$1,
|
|
827
|
+
boolean: boolean$1,
|
|
828
|
+
float32: float32$1,
|
|
829
|
+
float64: float64$1,
|
|
792
830
|
int16: int16$1,
|
|
793
|
-
uint16: uint16$1,
|
|
794
831
|
int32: int32$1,
|
|
795
|
-
uint32: uint32$1,
|
|
796
832
|
int64: int64$1,
|
|
833
|
+
int8: int8$1,
|
|
834
|
+
number: number$1,
|
|
835
|
+
string: string$1,
|
|
836
|
+
uint16: uint16$1,
|
|
837
|
+
uint32: uint32$1,
|
|
797
838
|
uint64: uint64$1,
|
|
798
|
-
|
|
799
|
-
|
|
839
|
+
uint8: uint8$1,
|
|
840
|
+
utf8Length: utf8Length,
|
|
841
|
+
utf8Write: utf8Write,
|
|
800
842
|
writeFloat32: writeFloat32,
|
|
801
|
-
writeFloat64: writeFloat64
|
|
802
|
-
boolean: boolean$1,
|
|
803
|
-
string: string$1,
|
|
804
|
-
number: number$1
|
|
843
|
+
writeFloat64: writeFloat64
|
|
805
844
|
});
|
|
806
845
|
|
|
807
846
|
class EncodeSchemaError extends Error {
|
|
@@ -943,6 +982,18 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
|
|
|
943
982
|
}
|
|
944
983
|
const type = changeTree.getType(field);
|
|
945
984
|
const value = changeTree.getValue(field);
|
|
985
|
+
// try { throw new Error(); } catch (e) {
|
|
986
|
+
// // only print if not coming from Reflection.ts
|
|
987
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
988
|
+
// console.log("encodeKeyValueOperation -> ", {
|
|
989
|
+
// ref: changeTree.ref.constructor.name,
|
|
990
|
+
// field,
|
|
991
|
+
// operation: OPERATION[operation],
|
|
992
|
+
// value: value?.toJSON(),
|
|
993
|
+
// items: ref.toJSON(),
|
|
994
|
+
// });
|
|
995
|
+
// }
|
|
996
|
+
// }
|
|
946
997
|
// TODO: inline this function call small performance gain
|
|
947
998
|
encodeValue(encoder, bytes, ref, type, value, field, operation, it);
|
|
948
999
|
};
|
|
@@ -952,15 +1003,19 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
|
|
|
952
1003
|
*/
|
|
953
1004
|
const encodeArray = function (encoder, bytes, changeTree, field, operation, it, isEncodeAll, hasView) {
|
|
954
1005
|
const ref = changeTree.ref;
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1006
|
+
const useOperationByRefId = hasView && changeTree.isFiltered && (typeof (changeTree.getType(field)) !== "string");
|
|
1007
|
+
let refOrIndex;
|
|
1008
|
+
if (useOperationByRefId) {
|
|
1009
|
+
refOrIndex = ref['tmpItems'][field][$changes].refId;
|
|
1010
|
+
if (operation === OPERATION.DELETE) {
|
|
1011
|
+
operation = OPERATION.DELETE_BY_REFID;
|
|
1012
|
+
}
|
|
1013
|
+
else if (operation === OPERATION.ADD) {
|
|
1014
|
+
operation = OPERATION.ADD_BY_REFID;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
else {
|
|
1018
|
+
refOrIndex = field;
|
|
964
1019
|
}
|
|
965
1020
|
// encode operation
|
|
966
1021
|
bytes[it.offset++] = operation & 255;
|
|
@@ -969,7 +1024,7 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
969
1024
|
return;
|
|
970
1025
|
}
|
|
971
1026
|
// encode index
|
|
972
|
-
number$1(bytes,
|
|
1027
|
+
number$1(bytes, refOrIndex, it);
|
|
973
1028
|
// Do not encode value for DELETE operations
|
|
974
1029
|
if (operation === OPERATION.DELETE) {
|
|
975
1030
|
return;
|
|
@@ -1009,9 +1064,9 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
1009
1064
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1010
1065
|
* SOFTWARE
|
|
1011
1066
|
*/
|
|
1012
|
-
function utf8Read(bytes,
|
|
1067
|
+
function utf8Read(bytes, it, length) {
|
|
1013
1068
|
var string = '', chr = 0;
|
|
1014
|
-
for (var i = offset, end = offset + length; i < end; i++) {
|
|
1069
|
+
for (var i = it.offset, end = it.offset + length; i < end; i++) {
|
|
1015
1070
|
var byte = bytes[i];
|
|
1016
1071
|
if ((byte & 0x80) === 0x00) {
|
|
1017
1072
|
string += String.fromCharCode(byte);
|
|
@@ -1046,6 +1101,7 @@ function utf8Read(bytes, offset, length) {
|
|
|
1046
1101
|
// (do not throw error to avoid server/client from crashing due to hack attemps)
|
|
1047
1102
|
// throw new Error('Invalid byte ' + byte.toString(16));
|
|
1048
1103
|
}
|
|
1104
|
+
it.offset += length;
|
|
1049
1105
|
return string;
|
|
1050
1106
|
}
|
|
1051
1107
|
function int8(bytes, it) {
|
|
@@ -1113,9 +1169,7 @@ function string(bytes, it) {
|
|
|
1113
1169
|
else if (prefix === 0xdb) {
|
|
1114
1170
|
length = uint32(bytes, it);
|
|
1115
1171
|
}
|
|
1116
|
-
|
|
1117
|
-
it.offset += length;
|
|
1118
|
-
return value;
|
|
1172
|
+
return utf8Read(bytes, it, length);
|
|
1119
1173
|
}
|
|
1120
1174
|
function stringCheck(bytes, it) {
|
|
1121
1175
|
const prefix = bytes[it.offset];
|
|
@@ -1219,30 +1273,31 @@ function switchStructureCheck(bytes, it) {
|
|
|
1219
1273
|
|
|
1220
1274
|
var decode = /*#__PURE__*/Object.freeze({
|
|
1221
1275
|
__proto__: null,
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
int16: int16,
|
|
1225
|
-
uint16: uint16,
|
|
1226
|
-
int32: int32,
|
|
1227
|
-
uint32: uint32,
|
|
1276
|
+
arrayCheck: arrayCheck,
|
|
1277
|
+
boolean: boolean,
|
|
1228
1278
|
float32: float32,
|
|
1229
1279
|
float64: float64,
|
|
1280
|
+
int16: int16,
|
|
1281
|
+
int32: int32,
|
|
1230
1282
|
int64: int64,
|
|
1231
|
-
|
|
1283
|
+
int8: int8,
|
|
1284
|
+
number: number,
|
|
1285
|
+
numberCheck: numberCheck,
|
|
1232
1286
|
readFloat32: readFloat32,
|
|
1233
1287
|
readFloat64: readFloat64,
|
|
1234
|
-
boolean: boolean,
|
|
1235
1288
|
string: string,
|
|
1236
1289
|
stringCheck: stringCheck,
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1290
|
+
switchStructureCheck: switchStructureCheck,
|
|
1291
|
+
uint16: uint16,
|
|
1292
|
+
uint32: uint32,
|
|
1293
|
+
uint64: uint64,
|
|
1294
|
+
uint8: uint8,
|
|
1295
|
+
utf8Read: utf8Read
|
|
1241
1296
|
});
|
|
1242
1297
|
|
|
1243
1298
|
const DEFINITION_MISMATCH = -1;
|
|
1244
1299
|
function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges) {
|
|
1245
|
-
const $root = decoder
|
|
1300
|
+
const $root = decoder.root;
|
|
1246
1301
|
const previousValue = ref[$getByIndex](index);
|
|
1247
1302
|
let value;
|
|
1248
1303
|
if ((operation & OPERATION.DELETE) === OPERATION.DELETE) {
|
|
@@ -1428,7 +1483,8 @@ const decodeKeyValueOperation = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1428
1483
|
};
|
|
1429
1484
|
const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
1430
1485
|
// "uncompressed" index + operation (array/map items)
|
|
1431
|
-
|
|
1486
|
+
let operation = bytes[it.offset++];
|
|
1487
|
+
let index;
|
|
1432
1488
|
if (operation === OPERATION.CLEAR) {
|
|
1433
1489
|
//
|
|
1434
1490
|
// When decoding:
|
|
@@ -1442,8 +1498,8 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1442
1498
|
else if (operation === OPERATION.DELETE_BY_REFID) {
|
|
1443
1499
|
// TODO: refactor here, try to follow same flow as below
|
|
1444
1500
|
const refId = number(bytes, it);
|
|
1445
|
-
const previousValue = decoder
|
|
1446
|
-
|
|
1501
|
+
const previousValue = decoder.root.refs.get(refId);
|
|
1502
|
+
index = ref.findIndex((value) => value === previousValue);
|
|
1447
1503
|
ref[$deleteByIndex](index);
|
|
1448
1504
|
allChanges.push({
|
|
1449
1505
|
ref,
|
|
@@ -1456,7 +1512,18 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1456
1512
|
});
|
|
1457
1513
|
return;
|
|
1458
1514
|
}
|
|
1459
|
-
|
|
1515
|
+
else if (operation === OPERATION.ADD_BY_REFID) {
|
|
1516
|
+
// operation = OPERATION.ADD;
|
|
1517
|
+
const refId = number(bytes, it);
|
|
1518
|
+
const itemByRefId = decoder.root.refs.get(refId);
|
|
1519
|
+
// use existing index, or push new value
|
|
1520
|
+
index = (itemByRefId)
|
|
1521
|
+
? ref.findIndex((value) => value === itemByRefId)
|
|
1522
|
+
: ref.length;
|
|
1523
|
+
}
|
|
1524
|
+
else {
|
|
1525
|
+
index = number(bytes, it);
|
|
1526
|
+
}
|
|
1460
1527
|
const type = ref[$childType];
|
|
1461
1528
|
let dynamicIndex = index;
|
|
1462
1529
|
const { value, previousValue } = decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges);
|
|
@@ -1865,14 +1932,6 @@ class ArraySchema {
|
|
|
1865
1932
|
lastIndexOf(searchElement, fromIndex = this.length - 1) {
|
|
1866
1933
|
return this.items.lastIndexOf(searchElement, fromIndex);
|
|
1867
1934
|
}
|
|
1868
|
-
/**
|
|
1869
|
-
* Determines whether all the members of an array satisfy the specified test.
|
|
1870
|
-
* @param callbackfn A function that accepts up to three arguments. The every method calls
|
|
1871
|
-
* the callbackfn function for each element in the array until the callbackfn returns a value
|
|
1872
|
-
* which is coercible to the Boolean value false, or until the end of the array.
|
|
1873
|
-
* @param thisArg An object to which the this keyword can refer in the callbackfn function.
|
|
1874
|
-
* If thisArg is omitted, undefined is used as the this value.
|
|
1875
|
-
*/
|
|
1876
1935
|
every(callbackfn, thisArg) {
|
|
1877
1936
|
return this.items.every(callbackfn, thisArg);
|
|
1878
1937
|
}
|
|
@@ -2551,6 +2610,7 @@ function view(tag = DEFAULT_VIEW_TAG) {
|
|
|
2551
2610
|
const constructor = target.constructor;
|
|
2552
2611
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
2553
2612
|
const parentMetadata = parentClass[Symbol.metadata];
|
|
2613
|
+
// TODO: use Metadata.initialize()
|
|
2554
2614
|
const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
|
|
2555
2615
|
if (!metadata[fieldName]) {
|
|
2556
2616
|
//
|
|
@@ -2575,8 +2635,8 @@ function type(type, options) {
|
|
|
2575
2635
|
// for inheritance support
|
|
2576
2636
|
TypeContext.register(constructor);
|
|
2577
2637
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
2578
|
-
const parentMetadata = parentClass[Symbol.metadata];
|
|
2579
|
-
const metadata =
|
|
2638
|
+
const parentMetadata = parentClass && parentClass[Symbol.metadata];
|
|
2639
|
+
const metadata = Metadata.initialize(constructor, parentMetadata);
|
|
2580
2640
|
let fieldIndex;
|
|
2581
2641
|
/**
|
|
2582
2642
|
* skip if descriptor already exists for this field (`@deprecated()`)
|
|
@@ -3349,6 +3409,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
3349
3409
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
3350
3410
|
PERFORMANCE OF THIS SOFTWARE.
|
|
3351
3411
|
***************************************************************************** */
|
|
3412
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
3413
|
+
|
|
3352
3414
|
|
|
3353
3415
|
function __decorate(decorators, target, key, desc) {
|
|
3354
3416
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -3380,11 +3442,14 @@ class Encoder {
|
|
|
3380
3442
|
setRoot(state) {
|
|
3381
3443
|
this.root = new Root();
|
|
3382
3444
|
this.state = state;
|
|
3445
|
+
// Workaround to allow using an empty Schema.
|
|
3446
|
+
if (state.constructor[Symbol.metadata] === undefined) {
|
|
3447
|
+
Metadata.init(state);
|
|
3448
|
+
}
|
|
3383
3449
|
state[$changes].setRoot(this.root);
|
|
3384
3450
|
}
|
|
3385
|
-
encode(it = { offset: 0 }, view,
|
|
3451
|
+
encode(it = { offset: 0 }, view, buffer = this.sharedBuffer, changeTrees = this.root.changes, isEncodeAll = this.root.allChanges === changeTrees) {
|
|
3386
3452
|
const initialOffset = it.offset; // cache current offset in case we need to resize the buffer
|
|
3387
|
-
const isEncodeAll = this.root.allChanges === changeTrees;
|
|
3388
3453
|
const hasView = (view !== undefined);
|
|
3389
3454
|
const rootChangeTree = this.state[$changes];
|
|
3390
3455
|
const changeTreesIterator = changeTrees.entries();
|
|
@@ -3393,6 +3458,12 @@ class Encoder {
|
|
|
3393
3458
|
const ctor = ref['constructor'];
|
|
3394
3459
|
const encoder = ctor[$encoder];
|
|
3395
3460
|
const filter = ctor[$filter];
|
|
3461
|
+
// try { throw new Error(); } catch (e) {
|
|
3462
|
+
// // only print if not coming from Reflection.ts
|
|
3463
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
3464
|
+
// console.log("ChangeTree:", { ref: ref.constructor.name, });
|
|
3465
|
+
// }
|
|
3466
|
+
// }
|
|
3396
3467
|
if (hasView) {
|
|
3397
3468
|
if (!view.items.has(changeTree)) {
|
|
3398
3469
|
view.invisible.add(changeTree);
|
|
@@ -3404,8 +3475,8 @@ class Encoder {
|
|
|
3404
3475
|
}
|
|
3405
3476
|
// skip root `refId` if it's the first change tree
|
|
3406
3477
|
if (it.offset !== initialOffset || changeTree !== rootChangeTree) {
|
|
3407
|
-
|
|
3408
|
-
number$1(
|
|
3478
|
+
buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
|
|
3479
|
+
number$1(buffer, changeTree.refId, it);
|
|
3409
3480
|
}
|
|
3410
3481
|
const changesIterator = changes.entries();
|
|
3411
3482
|
for (const [fieldIndex, operation] of changesIterator) {
|
|
@@ -3422,22 +3493,31 @@ class Encoder {
|
|
|
3422
3493
|
// view?.invisible.add(changeTree);
|
|
3423
3494
|
continue;
|
|
3424
3495
|
}
|
|
3425
|
-
//
|
|
3426
|
-
//
|
|
3427
|
-
//
|
|
3428
|
-
//
|
|
3429
|
-
//
|
|
3430
|
-
|
|
3496
|
+
// try { throw new Error(); } catch (e) {
|
|
3497
|
+
// // only print if not coming from Reflection.ts
|
|
3498
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
3499
|
+
// console.log("WILL ENCODE", {
|
|
3500
|
+
// ref: changeTree.ref.constructor.name,
|
|
3501
|
+
// fieldIndex,
|
|
3502
|
+
// operation: OPERATION[operation],
|
|
3503
|
+
// });
|
|
3504
|
+
// }
|
|
3505
|
+
// }
|
|
3506
|
+
encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
|
|
3431
3507
|
}
|
|
3432
3508
|
}
|
|
3433
|
-
if (it.offset >
|
|
3434
|
-
const newSize = getNextPowerOf2(
|
|
3435
|
-
console.warn("@colyseus/schema encode buffer overflow. Current buffer size: " +
|
|
3509
|
+
if (it.offset > buffer.byteLength) {
|
|
3510
|
+
const newSize = getNextPowerOf2(buffer.byteLength * 2);
|
|
3511
|
+
console.warn("@colyseus/schema encode buffer overflow. Current buffer size: " + buffer.byteLength + ", encoding offset: " + it.offset + ", new size: " + newSize);
|
|
3436
3512
|
//
|
|
3437
3513
|
// resize buffer and re-encode (TODO: can we avoid re-encoding here?)
|
|
3438
3514
|
//
|
|
3439
|
-
|
|
3440
|
-
|
|
3515
|
+
buffer = Buffer.allocUnsafeSlow(newSize);
|
|
3516
|
+
// assign resized buffer to local sharedBuffer
|
|
3517
|
+
if (buffer === this.sharedBuffer) {
|
|
3518
|
+
this.sharedBuffer = buffer;
|
|
3519
|
+
}
|
|
3520
|
+
return this.encode({ offset: initialOffset }, view, buffer, changeTrees, isEncodeAll);
|
|
3441
3521
|
}
|
|
3442
3522
|
else {
|
|
3443
3523
|
//
|
|
@@ -3449,38 +3529,37 @@ class Encoder {
|
|
|
3449
3529
|
//
|
|
3450
3530
|
this.onEndEncode(changeTrees);
|
|
3451
3531
|
}
|
|
3452
|
-
|
|
3453
|
-
return bytes.slice(0, it.offset);
|
|
3532
|
+
return buffer.subarray(0, it.offset);
|
|
3454
3533
|
}
|
|
3455
3534
|
}
|
|
3456
|
-
encodeAll(it = { offset: 0 }) {
|
|
3457
|
-
// console.log(`encodeAll(), this
|
|
3458
|
-
// Array.from(this
|
|
3459
|
-
// console.log("->", item[0].
|
|
3535
|
+
encodeAll(it = { offset: 0 }, buffer = this.sharedBuffer) {
|
|
3536
|
+
// console.log(`encodeAll(), this.root.allChanges (${this.root.allChanges.size})`);
|
|
3537
|
+
// Array.from(this.root.allChanges.entries()).map((item) => {
|
|
3538
|
+
// console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
|
|
3460
3539
|
// });
|
|
3461
|
-
return this.encode(it, undefined,
|
|
3540
|
+
return this.encode(it, undefined, buffer, this.root.allChanges, true);
|
|
3462
3541
|
}
|
|
3463
3542
|
encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3464
3543
|
const viewOffset = it.offset;
|
|
3465
|
-
// console.log(`encodeAllView(), this
|
|
3544
|
+
// console.log(`encodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
|
|
3466
3545
|
// this.debugAllFilteredChanges();
|
|
3467
3546
|
// try to encode "filtered" changes
|
|
3468
|
-
this.encode(it, view, bytes, this.root.allFilteredChanges);
|
|
3547
|
+
this.encode(it, view, bytes, this.root.allFilteredChanges, true);
|
|
3469
3548
|
return Buffer.concat([
|
|
3470
|
-
bytes.
|
|
3471
|
-
bytes.
|
|
3549
|
+
bytes.subarray(0, sharedOffset),
|
|
3550
|
+
bytes.subarray(viewOffset, it.offset)
|
|
3472
3551
|
]);
|
|
3473
3552
|
}
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3553
|
+
debugAllFilteredChanges() {
|
|
3554
|
+
Array.from(this.root.allFilteredChanges.entries()).map((item) => {
|
|
3555
|
+
console.log("->", { refId: item[0].refId, changes: item[1].size }, item[0].ref.toJSON());
|
|
3556
|
+
if (Array.isArray(item[0].ref.toJSON())) {
|
|
3557
|
+
item[1].forEach((op, key) => {
|
|
3558
|
+
console.log(" ->", { key, op: OPERATION[op] });
|
|
3559
|
+
});
|
|
3560
|
+
}
|
|
3561
|
+
});
|
|
3562
|
+
}
|
|
3484
3563
|
encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3485
3564
|
const viewOffset = it.offset;
|
|
3486
3565
|
// try to encode "filtered" changes
|
|
@@ -3512,8 +3591,8 @@ class Encoder {
|
|
|
3512
3591
|
// clear "view" changes after encoding
|
|
3513
3592
|
view.changes.clear();
|
|
3514
3593
|
return Buffer.concat([
|
|
3515
|
-
bytes.
|
|
3516
|
-
bytes.
|
|
3594
|
+
bytes.subarray(0, sharedOffset),
|
|
3595
|
+
bytes.subarray(viewOffset, it.offset)
|
|
3517
3596
|
]);
|
|
3518
3597
|
}
|
|
3519
3598
|
onEndEncode(changeTrees = this.root.changes) {
|
|
@@ -3692,12 +3771,12 @@ class Decoder {
|
|
|
3692
3771
|
}
|
|
3693
3772
|
setRoot(root) {
|
|
3694
3773
|
this.state = root;
|
|
3695
|
-
this
|
|
3696
|
-
this
|
|
3774
|
+
this.root = new ReferenceTracker();
|
|
3775
|
+
this.root.addRef(0, root);
|
|
3697
3776
|
}
|
|
3698
3777
|
decode(bytes, it = { offset: 0 }, ref = this.state) {
|
|
3699
3778
|
const allChanges = [];
|
|
3700
|
-
const $root = this
|
|
3779
|
+
const $root = this.root;
|
|
3701
3780
|
const totalBytes = bytes.byteLength;
|
|
3702
3781
|
let decoder = ref['constructor'][$decoder];
|
|
3703
3782
|
this.currentRefId = 0;
|
|
@@ -3778,7 +3857,7 @@ class Decoder {
|
|
|
3778
3857
|
previousValue: value
|
|
3779
3858
|
});
|
|
3780
3859
|
if (needRemoveRef) {
|
|
3781
|
-
this
|
|
3860
|
+
this.root.removeRef(this.root.refIds.get(value));
|
|
3782
3861
|
}
|
|
3783
3862
|
});
|
|
3784
3863
|
}
|
|
@@ -3818,7 +3897,7 @@ class Reflection extends Schema {
|
|
|
3818
3897
|
super(...arguments);
|
|
3819
3898
|
this.types = new ArraySchema();
|
|
3820
3899
|
}
|
|
3821
|
-
static encode(instance, context) {
|
|
3900
|
+
static encode(instance, context, it = { offset: 0 }) {
|
|
3822
3901
|
if (!context) {
|
|
3823
3902
|
context = new TypeContext(instance.constructor);
|
|
3824
3903
|
}
|
|
@@ -3875,7 +3954,6 @@ class Reflection extends Schema {
|
|
|
3875
3954
|
}
|
|
3876
3955
|
buildType(type, klass[Symbol.metadata]);
|
|
3877
3956
|
}
|
|
3878
|
-
const it = { offset: 0 };
|
|
3879
3957
|
const buf = encoder.encodeAll(it);
|
|
3880
3958
|
return Buffer.from(buf, 0, it.offset);
|
|
3881
3959
|
}
|
|
@@ -3883,59 +3961,298 @@ class Reflection extends Schema {
|
|
|
3883
3961
|
const reflection = new Reflection();
|
|
3884
3962
|
const reflectionDecoder = new Decoder(reflection);
|
|
3885
3963
|
reflectionDecoder.decode(bytes, it);
|
|
3886
|
-
const
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
const
|
|
3964
|
+
const typeContext = new TypeContext();
|
|
3965
|
+
// 1st pass, initialize metadata + inheritance
|
|
3966
|
+
reflection.types.forEach((reflectionType) => {
|
|
3967
|
+
const parentClass = typeContext.get(reflectionType.extendsId) ?? Schema;
|
|
3968
|
+
const schema = class _ extends parentClass {
|
|
3890
3969
|
};
|
|
3891
|
-
|
|
3892
|
-
const _metadata = parentKlass && parentKlass[Symbol.metadata] || Object.create(null);
|
|
3893
|
-
Object.defineProperty(schema, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
3970
|
+
const parentMetadata = parentClass[Symbol.metadata];
|
|
3894
3971
|
// register for inheritance support
|
|
3895
3972
|
TypeContext.register(schema);
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
return types;
|
|
3973
|
+
// for inheritance support
|
|
3974
|
+
Metadata.initialize(schema, parentMetadata);
|
|
3975
|
+
typeContext.add(schema, reflectionType.id);
|
|
3900
3976
|
}, {});
|
|
3977
|
+
// 2nd pass, set fields
|
|
3901
3978
|
reflection.types.forEach((reflectionType) => {
|
|
3902
|
-
const schemaType =
|
|
3979
|
+
const schemaType = typeContext.get(reflectionType.id);
|
|
3903
3980
|
const metadata = schemaType[Symbol.metadata];
|
|
3904
|
-
|
|
3905
|
-
const parentFieldIndex =
|
|
3981
|
+
// FIXME: use metadata[-1] to get field count
|
|
3982
|
+
const parentFieldIndex = 0;
|
|
3983
|
+
// console.log("--------------------");
|
|
3984
|
+
// // console.log("reflectionType", reflectionType.toJSON());
|
|
3985
|
+
// console.log("reflectionType.fields", reflectionType.fields.toJSON());
|
|
3986
|
+
// console.log("parentFieldIndex", parentFieldIndex);
|
|
3987
|
+
//
|
|
3988
|
+
// FIXME: set fields using parentKlass as well
|
|
3989
|
+
// currently the fields are duplicated on inherited classes
|
|
3990
|
+
//
|
|
3991
|
+
// // const parentKlass = reflection.types[reflectionType.extendsId];
|
|
3992
|
+
// // parentKlass.fields
|
|
3906
3993
|
reflectionType.fields.forEach((field, i) => {
|
|
3907
3994
|
const fieldIndex = parentFieldIndex + i;
|
|
3908
3995
|
if (field.referencedType !== undefined) {
|
|
3909
3996
|
let fieldType = field.type;
|
|
3910
|
-
let refType =
|
|
3997
|
+
let refType = typeContext.get(field.referencedType);
|
|
3911
3998
|
// map or array of primitive type (-1)
|
|
3912
3999
|
if (!refType) {
|
|
3913
4000
|
const typeInfo = field.type.split(":");
|
|
3914
4001
|
fieldType = typeInfo[0];
|
|
3915
|
-
refType = typeInfo[1];
|
|
4002
|
+
refType = typeInfo[1]; // string
|
|
3916
4003
|
}
|
|
3917
4004
|
if (fieldType === "ref") {
|
|
3918
|
-
// type(refType)(schemaType.prototype, field.name);
|
|
3919
4005
|
Metadata.addField(metadata, fieldIndex, field.name, refType);
|
|
3920
4006
|
}
|
|
3921
4007
|
else {
|
|
3922
|
-
// type({ [fieldType]: refType } as DefinitionType)(schemaType.prototype, field.name);
|
|
3923
4008
|
Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType });
|
|
3924
4009
|
}
|
|
3925
4010
|
}
|
|
3926
4011
|
else {
|
|
3927
|
-
// type(field.type as PrimitiveType)(schemaType.prototype, field.name);
|
|
3928
4012
|
Metadata.addField(metadata, fieldIndex, field.name, field.type);
|
|
3929
4013
|
}
|
|
3930
4014
|
});
|
|
3931
4015
|
});
|
|
3932
|
-
|
|
4016
|
+
// @ts-ignore
|
|
4017
|
+
return new (typeContext.get(0))();
|
|
3933
4018
|
}
|
|
3934
4019
|
}
|
|
3935
4020
|
__decorate([
|
|
3936
4021
|
type([ReflectionType])
|
|
3937
4022
|
], Reflection.prototype, "types", void 0);
|
|
3938
4023
|
|
|
4024
|
+
function getDecoderStateCallbacks(decoder) {
|
|
4025
|
+
const $root = decoder.root;
|
|
4026
|
+
const callbacks = $root.callbacks;
|
|
4027
|
+
let isTriggeringOnAdd = false;
|
|
4028
|
+
decoder.triggerChanges = function (allChanges) {
|
|
4029
|
+
const uniqueRefIds = new Set();
|
|
4030
|
+
for (let i = 0, l = allChanges.length; i < l; i++) {
|
|
4031
|
+
const change = allChanges[i];
|
|
4032
|
+
const refId = change.refId;
|
|
4033
|
+
const ref = change.ref;
|
|
4034
|
+
const $callbacks = callbacks[refId];
|
|
4035
|
+
if (!$callbacks) {
|
|
4036
|
+
continue;
|
|
4037
|
+
}
|
|
4038
|
+
//
|
|
4039
|
+
// trigger onRemove on child structure.
|
|
4040
|
+
//
|
|
4041
|
+
if ((change.op & OPERATION.DELETE) === OPERATION.DELETE &&
|
|
4042
|
+
change.previousValue instanceof Schema) {
|
|
4043
|
+
const deleteCallbacks = callbacks[$root.refIds.get(change.previousValue)]?.[OPERATION.DELETE];
|
|
4044
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
4045
|
+
deleteCallbacks[i]();
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
4048
|
+
if (ref instanceof Schema) {
|
|
4049
|
+
//
|
|
4050
|
+
// Handle schema instance
|
|
4051
|
+
//
|
|
4052
|
+
if (!uniqueRefIds.has(refId)) {
|
|
4053
|
+
// trigger onChange
|
|
4054
|
+
const replaceCallbacks = $callbacks?.[OPERATION.REPLACE];
|
|
4055
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
4056
|
+
replaceCallbacks[i]();
|
|
4057
|
+
// try {
|
|
4058
|
+
// } catch (e) {
|
|
4059
|
+
// console.error(e);
|
|
4060
|
+
// }
|
|
4061
|
+
}
|
|
4062
|
+
}
|
|
4063
|
+
if ($callbacks.hasOwnProperty(change.field)) {
|
|
4064
|
+
const fieldCallbacks = $callbacks[change.field];
|
|
4065
|
+
for (let i = fieldCallbacks?.length - 1; i >= 0; i--) {
|
|
4066
|
+
fieldCallbacks[i](change.value, change.previousValue);
|
|
4067
|
+
// try {
|
|
4068
|
+
// } catch (e) {
|
|
4069
|
+
// console.error(e);
|
|
4070
|
+
// }
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
else {
|
|
4075
|
+
//
|
|
4076
|
+
// Handle collection of items
|
|
4077
|
+
//
|
|
4078
|
+
if ((change.op & OPERATION.DELETE) === OPERATION.DELETE) {
|
|
4079
|
+
//
|
|
4080
|
+
// FIXME: `previousValue` should always be available.
|
|
4081
|
+
//
|
|
4082
|
+
if (change.previousValue !== undefined) {
|
|
4083
|
+
// triger onRemove
|
|
4084
|
+
const deleteCallbacks = $callbacks[OPERATION.DELETE];
|
|
4085
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
4086
|
+
deleteCallbacks[i](change.previousValue, change.dynamicIndex ?? change.field);
|
|
4087
|
+
}
|
|
4088
|
+
}
|
|
4089
|
+
// Handle DELETE_AND_ADD operations
|
|
4090
|
+
// FIXME: should we set "isTriggeringOnAdd" here?
|
|
4091
|
+
if ((change.op & OPERATION.ADD) === OPERATION.ADD) {
|
|
4092
|
+
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
4093
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4094
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
else if ((change.op & OPERATION.ADD) === OPERATION.ADD && change.previousValue === undefined) {
|
|
4099
|
+
// triger onAdd
|
|
4100
|
+
isTriggeringOnAdd = true;
|
|
4101
|
+
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
4102
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4103
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4104
|
+
}
|
|
4105
|
+
isTriggeringOnAdd = false;
|
|
4106
|
+
}
|
|
4107
|
+
// trigger onChange
|
|
4108
|
+
if (change.value !== change.previousValue) {
|
|
4109
|
+
const replaceCallbacks = $callbacks[OPERATION.REPLACE];
|
|
4110
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
4111
|
+
replaceCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
uniqueRefIds.add(refId);
|
|
4116
|
+
}
|
|
4117
|
+
};
|
|
4118
|
+
function getProxy(metadataOrType, context) {
|
|
4119
|
+
let metadata = context.instance?.constructor[Symbol.metadata] || metadataOrType;
|
|
4120
|
+
let isCollection = ((context.instance && typeof (context.instance['forEach']) === "function") ||
|
|
4121
|
+
(metadataOrType && typeof (metadataOrType[Symbol.metadata]) === "undefined"));
|
|
4122
|
+
if (metadata && !isCollection) {
|
|
4123
|
+
const onAdd = function (ref, prop, callback, immediate) {
|
|
4124
|
+
// immediate trigger
|
|
4125
|
+
if (immediate &&
|
|
4126
|
+
context.instance[prop] !== undefined &&
|
|
4127
|
+
!isTriggeringOnAdd // FIXME: This is a workaround (https://github.com/colyseus/schema/issues/147)
|
|
4128
|
+
) {
|
|
4129
|
+
callback(context.instance[prop], undefined);
|
|
4130
|
+
}
|
|
4131
|
+
return $root.addCallback($root.refIds.get(ref), prop, callback);
|
|
4132
|
+
};
|
|
4133
|
+
/**
|
|
4134
|
+
* Schema instances
|
|
4135
|
+
*/
|
|
4136
|
+
return new Proxy({
|
|
4137
|
+
listen: function listen(prop, callback, immediate = true) {
|
|
4138
|
+
if (context.instance) {
|
|
4139
|
+
return onAdd(context.instance, prop, callback, immediate);
|
|
4140
|
+
}
|
|
4141
|
+
else {
|
|
4142
|
+
// collection instance not received yet
|
|
4143
|
+
context.onInstanceAvailable((ref, existing) => onAdd(ref, prop, callback, immediate && existing));
|
|
4144
|
+
}
|
|
4145
|
+
},
|
|
4146
|
+
onChange: function onChange(callback) {
|
|
4147
|
+
return $root.addCallback($root.refIds.get(context.instance), OPERATION.REPLACE, callback);
|
|
4148
|
+
},
|
|
4149
|
+
bindTo: function bindTo(targetObject, properties) {
|
|
4150
|
+
//
|
|
4151
|
+
// TODO: refactor this implementation. There is room for improvement here.
|
|
4152
|
+
//
|
|
4153
|
+
if (!properties) {
|
|
4154
|
+
properties = Object.keys(metadata);
|
|
4155
|
+
}
|
|
4156
|
+
return $root.addCallback($root.refIds.get(context.instance), OPERATION.REPLACE, () => {
|
|
4157
|
+
properties.forEach((prop) => targetObject[prop] = context.instance[prop]);
|
|
4158
|
+
});
|
|
4159
|
+
}
|
|
4160
|
+
}, {
|
|
4161
|
+
get(target, prop) {
|
|
4162
|
+
if (metadata[prop]) {
|
|
4163
|
+
const instance = context.instance?.[prop];
|
|
4164
|
+
const onInstanceAvailable = ((callback) => {
|
|
4165
|
+
const unbind = $(context.instance).listen(prop, (value, _) => {
|
|
4166
|
+
callback(value, false);
|
|
4167
|
+
// FIXME: by "unbinding" the callback here,
|
|
4168
|
+
// it will not support when the server
|
|
4169
|
+
// re-instantiates the instance.
|
|
4170
|
+
//
|
|
4171
|
+
unbind?.();
|
|
4172
|
+
}, false);
|
|
4173
|
+
// has existing value
|
|
4174
|
+
if ($root.refIds.get(instance) !== undefined) {
|
|
4175
|
+
callback(instance, true);
|
|
4176
|
+
}
|
|
4177
|
+
});
|
|
4178
|
+
return getProxy(metadata[prop].type, {
|
|
4179
|
+
instance,
|
|
4180
|
+
parentInstance: context.instance,
|
|
4181
|
+
onInstanceAvailable,
|
|
4182
|
+
});
|
|
4183
|
+
}
|
|
4184
|
+
else {
|
|
4185
|
+
// accessing the function
|
|
4186
|
+
return target[prop];
|
|
4187
|
+
}
|
|
4188
|
+
},
|
|
4189
|
+
has(target, prop) { return metadata[prop] !== undefined; },
|
|
4190
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4191
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4192
|
+
});
|
|
4193
|
+
}
|
|
4194
|
+
else {
|
|
4195
|
+
/**
|
|
4196
|
+
* Collection instances
|
|
4197
|
+
*/
|
|
4198
|
+
const onAdd = function (ref, callback, immediate) {
|
|
4199
|
+
// Trigger callback on existing items
|
|
4200
|
+
if (immediate) {
|
|
4201
|
+
ref.forEach((v, k) => callback(v, k));
|
|
4202
|
+
}
|
|
4203
|
+
return $root.addCallback($root.refIds.get(ref), OPERATION.ADD, callback);
|
|
4204
|
+
};
|
|
4205
|
+
const onRemove = function (ref, callback) {
|
|
4206
|
+
return $root.addCallback($root.refIds.get(ref), OPERATION.DELETE, callback);
|
|
4207
|
+
};
|
|
4208
|
+
return new Proxy({
|
|
4209
|
+
onAdd: function (callback, immediate = true) {
|
|
4210
|
+
//
|
|
4211
|
+
// https://github.com/colyseus/schema/issues/147
|
|
4212
|
+
// If parent instance has "onAdd" registered, avoid triggering immediate callback.
|
|
4213
|
+
//
|
|
4214
|
+
// FIXME: "isTriggeringOnAdd" is a workaround. We should find a better way to handle this.
|
|
4215
|
+
//
|
|
4216
|
+
if (context.onInstanceAvailable) {
|
|
4217
|
+
// collection instance not received yet
|
|
4218
|
+
context.onInstanceAvailable((ref, existing) => onAdd(ref, callback, immediate && existing && !isTriggeringOnAdd));
|
|
4219
|
+
}
|
|
4220
|
+
else if (context.instance) {
|
|
4221
|
+
onAdd(context.instance, callback, immediate && !isTriggeringOnAdd);
|
|
4222
|
+
}
|
|
4223
|
+
},
|
|
4224
|
+
onRemove: function (callback) {
|
|
4225
|
+
if (context.onInstanceAvailable) {
|
|
4226
|
+
// collection instance not received yet
|
|
4227
|
+
context.onInstanceAvailable((ref) => onRemove(ref, callback));
|
|
4228
|
+
}
|
|
4229
|
+
else if (context.instance) {
|
|
4230
|
+
onRemove(context.instance, callback);
|
|
4231
|
+
}
|
|
4232
|
+
},
|
|
4233
|
+
}, {
|
|
4234
|
+
get(target, prop) {
|
|
4235
|
+
if (!target[prop]) {
|
|
4236
|
+
throw new Error(`Can't access '${prop}' through callback proxy. access the instance directly.`);
|
|
4237
|
+
}
|
|
4238
|
+
return target[prop];
|
|
4239
|
+
},
|
|
4240
|
+
has(target, prop) { return target[prop] !== undefined; },
|
|
4241
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4242
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4243
|
+
});
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
function $(instance) {
|
|
4247
|
+
return getProxy(undefined, { instance });
|
|
4248
|
+
}
|
|
4249
|
+
return $;
|
|
4250
|
+
}
|
|
4251
|
+
|
|
4252
|
+
function getRawChangesCallback(decoder, callback) {
|
|
4253
|
+
decoder.triggerChanges = callback;
|
|
4254
|
+
}
|
|
4255
|
+
|
|
3939
4256
|
class StateView {
|
|
3940
4257
|
constructor() {
|
|
3941
4258
|
/**
|
|
@@ -3958,12 +4275,18 @@ class StateView {
|
|
|
3958
4275
|
console.warn("StateView#add(), invalid object:", obj);
|
|
3959
4276
|
return this;
|
|
3960
4277
|
}
|
|
4278
|
+
// FIXME: ArraySchema/MapSchema does not have metadata
|
|
4279
|
+
const metadata = obj.constructor[Symbol.metadata];
|
|
3961
4280
|
let changeTree = obj[$changes];
|
|
3962
4281
|
this.items.add(changeTree);
|
|
3963
4282
|
// Add children of this ChangeTree to this view
|
|
3964
|
-
changeTree.forEachChild((change,
|
|
3965
|
-
|
|
3966
|
-
|
|
4283
|
+
changeTree.forEachChild((change, index) => {
|
|
4284
|
+
// Do not ADD children that don't have the same tag
|
|
4285
|
+
if (metadata && metadata[metadata[index]].tag !== tag) {
|
|
4286
|
+
return;
|
|
4287
|
+
}
|
|
4288
|
+
this.add(change.ref, tag);
|
|
4289
|
+
});
|
|
3967
4290
|
// add parent ChangeTree's, if they are invisible to this view
|
|
3968
4291
|
// TODO: REFACTOR addParent()
|
|
3969
4292
|
this.addParent(changeTree, tag);
|
|
@@ -3990,7 +4313,6 @@ class StateView {
|
|
|
3990
4313
|
tags = this.tags.get(changeTree);
|
|
3991
4314
|
}
|
|
3992
4315
|
tags.add(tag);
|
|
3993
|
-
// console.log("BY TAG:", tag);
|
|
3994
4316
|
// Ref: add tagged properties
|
|
3995
4317
|
metadata?.[-3]?.[tag]?.forEach((index) => {
|
|
3996
4318
|
if (changeTree.getChange(index) !== OPERATION.DELETE) {
|
|
@@ -4129,5 +4451,5 @@ registerType("array", { constructor: ArraySchema });
|
|
|
4129
4451
|
registerType("set", { constructor: SetSchema });
|
|
4130
4452
|
registerType("collection", { constructor: CollectionSchema, });
|
|
4131
4453
|
|
|
4132
|
-
export { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $track, ArraySchema, ChangeTree, CollectionSchema, Decoder, Encoder, MapSchema, Metadata, OPERATION, Reflection, ReflectionField, ReflectionType, Schema, SetSchema, StateView, TypeContext, decode, decodeKeyValueOperation, decodeSchemaOperation, defineTypes, deprecated, dumpChanges, encode, encodeArray as encodeKeyValueOperation, encodeSchemaOperation, registerType, type, view };
|
|
4454
|
+
export { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $track, ArraySchema, ChangeTree, CollectionSchema, Decoder, Encoder, MapSchema, Metadata, OPERATION, Reflection, ReflectionField, ReflectionType, Schema, SetSchema, StateView, TypeContext, decode, decodeKeyValueOperation, decodeSchemaOperation, defineTypes, deprecated, dumpChanges, encode, encodeArray as encodeKeyValueOperation, encodeSchemaOperation, getDecoderStateCallbacks, getRawChangesCallback, registerType, type, view };
|
|
4133
4455
|
//# sourceMappingURL=index.mjs.map
|