@colyseus/schema 3.0.0-alpha.2 → 3.0.0-alpha.23
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 +476 -150
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +475 -149
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +476 -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/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 +22 -7
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/decoder/Decoder.d.ts +4 -5
- 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 +60 -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/debug.ts +56 -0
- package/src/decoder/DecodeOperation.ts +26 -6
- package/src/decoder/Decoder.ts +8 -8
- package/src/decoder/strategy/StateCallbacks.ts +93 -53
- package/src/encoder/ChangeTree.ts +2 -5
- package/src/encoder/EncodeOperation.ts +29 -12
- package/src/encoder/Encoder.ts +71 -42
- 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,35 @@ 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(
|
|
3509
|
+
if (it.offset > buffer.byteLength) {
|
|
3510
|
+
const newSize = getNextPowerOf2(buffer.byteLength * 2);
|
|
3511
|
+
console.warn(`@colyseus/schema buffer overflow. Encoded state is higher than default BUFFER_SIZE. Use the following to increase default BUFFER_SIZE:
|
|
3512
|
+
|
|
3513
|
+
import { Encoder } from "@colyseus/schema";
|
|
3514
|
+
Encoder.BUFFER_SIZE = ${Math.round(newSize / 1024)} * 1024; // ${Math.round(newSize / 1024)} KB
|
|
3515
|
+
`);
|
|
3436
3516
|
//
|
|
3437
3517
|
// resize buffer and re-encode (TODO: can we avoid re-encoding here?)
|
|
3438
3518
|
//
|
|
3439
|
-
|
|
3440
|
-
|
|
3519
|
+
buffer = Buffer.allocUnsafeSlow(newSize);
|
|
3520
|
+
// assign resized buffer to local sharedBuffer
|
|
3521
|
+
if (buffer === this.sharedBuffer) {
|
|
3522
|
+
this.sharedBuffer = buffer;
|
|
3523
|
+
}
|
|
3524
|
+
return this.encode({ offset: initialOffset }, view, buffer, changeTrees, isEncodeAll);
|
|
3441
3525
|
}
|
|
3442
3526
|
else {
|
|
3443
3527
|
//
|
|
@@ -3449,38 +3533,37 @@ class Encoder {
|
|
|
3449
3533
|
//
|
|
3450
3534
|
this.onEndEncode(changeTrees);
|
|
3451
3535
|
}
|
|
3452
|
-
|
|
3453
|
-
return bytes.slice(0, it.offset);
|
|
3536
|
+
return buffer.subarray(0, it.offset);
|
|
3454
3537
|
}
|
|
3455
3538
|
}
|
|
3456
|
-
encodeAll(it = { offset: 0 }) {
|
|
3457
|
-
// console.log(`encodeAll(), this
|
|
3458
|
-
// Array.from(this
|
|
3459
|
-
// console.log("->", item[0].
|
|
3539
|
+
encodeAll(it = { offset: 0 }, buffer = this.sharedBuffer) {
|
|
3540
|
+
// console.log(`encodeAll(), this.root.allChanges (${this.root.allChanges.size})`);
|
|
3541
|
+
// Array.from(this.root.allChanges.entries()).map((item) => {
|
|
3542
|
+
// console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
|
|
3460
3543
|
// });
|
|
3461
|
-
return this.encode(it, undefined,
|
|
3544
|
+
return this.encode(it, undefined, buffer, this.root.allChanges, true);
|
|
3462
3545
|
}
|
|
3463
3546
|
encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3464
3547
|
const viewOffset = it.offset;
|
|
3465
|
-
// console.log(`encodeAllView(), this
|
|
3548
|
+
// console.log(`encodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
|
|
3466
3549
|
// this.debugAllFilteredChanges();
|
|
3467
3550
|
// try to encode "filtered" changes
|
|
3468
|
-
this.encode(it, view, bytes, this.root.allFilteredChanges);
|
|
3551
|
+
this.encode(it, view, bytes, this.root.allFilteredChanges, true);
|
|
3469
3552
|
return Buffer.concat([
|
|
3470
|
-
bytes.
|
|
3471
|
-
bytes.
|
|
3553
|
+
bytes.subarray(0, sharedOffset),
|
|
3554
|
+
bytes.subarray(viewOffset, it.offset)
|
|
3472
3555
|
]);
|
|
3473
3556
|
}
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3557
|
+
debugAllFilteredChanges() {
|
|
3558
|
+
Array.from(this.root.allFilteredChanges.entries()).map((item) => {
|
|
3559
|
+
console.log("->", { refId: item[0].refId, changes: item[1].size }, item[0].ref.toJSON());
|
|
3560
|
+
if (Array.isArray(item[0].ref.toJSON())) {
|
|
3561
|
+
item[1].forEach((op, key) => {
|
|
3562
|
+
console.log(" ->", { key, op: OPERATION[op] });
|
|
3563
|
+
});
|
|
3564
|
+
}
|
|
3565
|
+
});
|
|
3566
|
+
}
|
|
3484
3567
|
encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3485
3568
|
const viewOffset = it.offset;
|
|
3486
3569
|
// try to encode "filtered" changes
|
|
@@ -3512,8 +3595,8 @@ class Encoder {
|
|
|
3512
3595
|
// clear "view" changes after encoding
|
|
3513
3596
|
view.changes.clear();
|
|
3514
3597
|
return Buffer.concat([
|
|
3515
|
-
bytes.
|
|
3516
|
-
bytes.
|
|
3598
|
+
bytes.subarray(0, sharedOffset),
|
|
3599
|
+
bytes.subarray(viewOffset, it.offset)
|
|
3517
3600
|
]);
|
|
3518
3601
|
}
|
|
3519
3602
|
onEndEncode(changeTrees = this.root.changes) {
|
|
@@ -3692,12 +3775,12 @@ class Decoder {
|
|
|
3692
3775
|
}
|
|
3693
3776
|
setRoot(root) {
|
|
3694
3777
|
this.state = root;
|
|
3695
|
-
this
|
|
3696
|
-
this
|
|
3778
|
+
this.root = new ReferenceTracker();
|
|
3779
|
+
this.root.addRef(0, root);
|
|
3697
3780
|
}
|
|
3698
3781
|
decode(bytes, it = { offset: 0 }, ref = this.state) {
|
|
3699
3782
|
const allChanges = [];
|
|
3700
|
-
const $root = this
|
|
3783
|
+
const $root = this.root;
|
|
3701
3784
|
const totalBytes = bytes.byteLength;
|
|
3702
3785
|
let decoder = ref['constructor'][$decoder];
|
|
3703
3786
|
this.currentRefId = 0;
|
|
@@ -3778,7 +3861,7 @@ class Decoder {
|
|
|
3778
3861
|
previousValue: value
|
|
3779
3862
|
});
|
|
3780
3863
|
if (needRemoveRef) {
|
|
3781
|
-
this
|
|
3864
|
+
this.root.removeRef(this.root.refIds.get(value));
|
|
3782
3865
|
}
|
|
3783
3866
|
});
|
|
3784
3867
|
}
|
|
@@ -3818,7 +3901,7 @@ class Reflection extends Schema {
|
|
|
3818
3901
|
super(...arguments);
|
|
3819
3902
|
this.types = new ArraySchema();
|
|
3820
3903
|
}
|
|
3821
|
-
static encode(instance, context) {
|
|
3904
|
+
static encode(instance, context, it = { offset: 0 }) {
|
|
3822
3905
|
if (!context) {
|
|
3823
3906
|
context = new TypeContext(instance.constructor);
|
|
3824
3907
|
}
|
|
@@ -3875,7 +3958,6 @@ class Reflection extends Schema {
|
|
|
3875
3958
|
}
|
|
3876
3959
|
buildType(type, klass[Symbol.metadata]);
|
|
3877
3960
|
}
|
|
3878
|
-
const it = { offset: 0 };
|
|
3879
3961
|
const buf = encoder.encodeAll(it);
|
|
3880
3962
|
return Buffer.from(buf, 0, it.offset);
|
|
3881
3963
|
}
|
|
@@ -3883,59 +3965,298 @@ class Reflection extends Schema {
|
|
|
3883
3965
|
const reflection = new Reflection();
|
|
3884
3966
|
const reflectionDecoder = new Decoder(reflection);
|
|
3885
3967
|
reflectionDecoder.decode(bytes, it);
|
|
3886
|
-
const
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
const
|
|
3968
|
+
const typeContext = new TypeContext();
|
|
3969
|
+
// 1st pass, initialize metadata + inheritance
|
|
3970
|
+
reflection.types.forEach((reflectionType) => {
|
|
3971
|
+
const parentClass = typeContext.get(reflectionType.extendsId) ?? Schema;
|
|
3972
|
+
const schema = class _ extends parentClass {
|
|
3890
3973
|
};
|
|
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 });
|
|
3974
|
+
const parentMetadata = parentClass[Symbol.metadata];
|
|
3894
3975
|
// register for inheritance support
|
|
3895
3976
|
TypeContext.register(schema);
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
return types;
|
|
3977
|
+
// for inheritance support
|
|
3978
|
+
Metadata.initialize(schema, parentMetadata);
|
|
3979
|
+
typeContext.add(schema, reflectionType.id);
|
|
3900
3980
|
}, {});
|
|
3981
|
+
// 2nd pass, set fields
|
|
3901
3982
|
reflection.types.forEach((reflectionType) => {
|
|
3902
|
-
const schemaType =
|
|
3983
|
+
const schemaType = typeContext.get(reflectionType.id);
|
|
3903
3984
|
const metadata = schemaType[Symbol.metadata];
|
|
3904
|
-
|
|
3905
|
-
const parentFieldIndex =
|
|
3985
|
+
// FIXME: use metadata[-1] to get field count
|
|
3986
|
+
const parentFieldIndex = 0;
|
|
3987
|
+
// console.log("--------------------");
|
|
3988
|
+
// // console.log("reflectionType", reflectionType.toJSON());
|
|
3989
|
+
// console.log("reflectionType.fields", reflectionType.fields.toJSON());
|
|
3990
|
+
// console.log("parentFieldIndex", parentFieldIndex);
|
|
3991
|
+
//
|
|
3992
|
+
// FIXME: set fields using parentKlass as well
|
|
3993
|
+
// currently the fields are duplicated on inherited classes
|
|
3994
|
+
//
|
|
3995
|
+
// // const parentKlass = reflection.types[reflectionType.extendsId];
|
|
3996
|
+
// // parentKlass.fields
|
|
3906
3997
|
reflectionType.fields.forEach((field, i) => {
|
|
3907
3998
|
const fieldIndex = parentFieldIndex + i;
|
|
3908
3999
|
if (field.referencedType !== undefined) {
|
|
3909
4000
|
let fieldType = field.type;
|
|
3910
|
-
let refType =
|
|
4001
|
+
let refType = typeContext.get(field.referencedType);
|
|
3911
4002
|
// map or array of primitive type (-1)
|
|
3912
4003
|
if (!refType) {
|
|
3913
4004
|
const typeInfo = field.type.split(":");
|
|
3914
4005
|
fieldType = typeInfo[0];
|
|
3915
|
-
refType = typeInfo[1];
|
|
4006
|
+
refType = typeInfo[1]; // string
|
|
3916
4007
|
}
|
|
3917
4008
|
if (fieldType === "ref") {
|
|
3918
|
-
// type(refType)(schemaType.prototype, field.name);
|
|
3919
4009
|
Metadata.addField(metadata, fieldIndex, field.name, refType);
|
|
3920
4010
|
}
|
|
3921
4011
|
else {
|
|
3922
|
-
// type({ [fieldType]: refType } as DefinitionType)(schemaType.prototype, field.name);
|
|
3923
4012
|
Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType });
|
|
3924
4013
|
}
|
|
3925
4014
|
}
|
|
3926
4015
|
else {
|
|
3927
|
-
// type(field.type as PrimitiveType)(schemaType.prototype, field.name);
|
|
3928
4016
|
Metadata.addField(metadata, fieldIndex, field.name, field.type);
|
|
3929
4017
|
}
|
|
3930
4018
|
});
|
|
3931
4019
|
});
|
|
3932
|
-
|
|
4020
|
+
// @ts-ignore
|
|
4021
|
+
return new (typeContext.get(0))();
|
|
3933
4022
|
}
|
|
3934
4023
|
}
|
|
3935
4024
|
__decorate([
|
|
3936
4025
|
type([ReflectionType])
|
|
3937
4026
|
], Reflection.prototype, "types", void 0);
|
|
3938
4027
|
|
|
4028
|
+
function getDecoderStateCallbacks(decoder) {
|
|
4029
|
+
const $root = decoder.root;
|
|
4030
|
+
const callbacks = $root.callbacks;
|
|
4031
|
+
let isTriggeringOnAdd = false;
|
|
4032
|
+
decoder.triggerChanges = function (allChanges) {
|
|
4033
|
+
const uniqueRefIds = new Set();
|
|
4034
|
+
for (let i = 0, l = allChanges.length; i < l; i++) {
|
|
4035
|
+
const change = allChanges[i];
|
|
4036
|
+
const refId = change.refId;
|
|
4037
|
+
const ref = change.ref;
|
|
4038
|
+
const $callbacks = callbacks[refId];
|
|
4039
|
+
if (!$callbacks) {
|
|
4040
|
+
continue;
|
|
4041
|
+
}
|
|
4042
|
+
//
|
|
4043
|
+
// trigger onRemove on child structure.
|
|
4044
|
+
//
|
|
4045
|
+
if ((change.op & OPERATION.DELETE) === OPERATION.DELETE &&
|
|
4046
|
+
change.previousValue instanceof Schema) {
|
|
4047
|
+
const deleteCallbacks = callbacks[$root.refIds.get(change.previousValue)]?.[OPERATION.DELETE];
|
|
4048
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
4049
|
+
deleteCallbacks[i]();
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
if (ref instanceof Schema) {
|
|
4053
|
+
//
|
|
4054
|
+
// Handle schema instance
|
|
4055
|
+
//
|
|
4056
|
+
if (!uniqueRefIds.has(refId)) {
|
|
4057
|
+
// trigger onChange
|
|
4058
|
+
const replaceCallbacks = $callbacks?.[OPERATION.REPLACE];
|
|
4059
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
4060
|
+
replaceCallbacks[i]();
|
|
4061
|
+
// try {
|
|
4062
|
+
// } catch (e) {
|
|
4063
|
+
// console.error(e);
|
|
4064
|
+
// }
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
if ($callbacks.hasOwnProperty(change.field)) {
|
|
4068
|
+
const fieldCallbacks = $callbacks[change.field];
|
|
4069
|
+
for (let i = fieldCallbacks?.length - 1; i >= 0; i--) {
|
|
4070
|
+
fieldCallbacks[i](change.value, change.previousValue);
|
|
4071
|
+
// try {
|
|
4072
|
+
// } catch (e) {
|
|
4073
|
+
// console.error(e);
|
|
4074
|
+
// }
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
}
|
|
4078
|
+
else {
|
|
4079
|
+
//
|
|
4080
|
+
// Handle collection of items
|
|
4081
|
+
//
|
|
4082
|
+
if ((change.op & OPERATION.DELETE) === OPERATION.DELETE) {
|
|
4083
|
+
//
|
|
4084
|
+
// FIXME: `previousValue` should always be available.
|
|
4085
|
+
//
|
|
4086
|
+
if (change.previousValue !== undefined) {
|
|
4087
|
+
// triger onRemove
|
|
4088
|
+
const deleteCallbacks = $callbacks[OPERATION.DELETE];
|
|
4089
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
4090
|
+
deleteCallbacks[i](change.previousValue, change.dynamicIndex ?? change.field);
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
// Handle DELETE_AND_ADD operations
|
|
4094
|
+
// FIXME: should we set "isTriggeringOnAdd" here?
|
|
4095
|
+
if ((change.op & OPERATION.ADD) === OPERATION.ADD) {
|
|
4096
|
+
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
4097
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4098
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
else if ((change.op & OPERATION.ADD) === OPERATION.ADD && change.previousValue === undefined) {
|
|
4103
|
+
// triger onAdd
|
|
4104
|
+
isTriggeringOnAdd = true;
|
|
4105
|
+
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
4106
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4107
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4108
|
+
}
|
|
4109
|
+
isTriggeringOnAdd = false;
|
|
4110
|
+
}
|
|
4111
|
+
// trigger onChange
|
|
4112
|
+
if (change.value !== change.previousValue) {
|
|
4113
|
+
const replaceCallbacks = $callbacks[OPERATION.REPLACE];
|
|
4114
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
4115
|
+
replaceCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
uniqueRefIds.add(refId);
|
|
4120
|
+
}
|
|
4121
|
+
};
|
|
4122
|
+
function getProxy(metadataOrType, context) {
|
|
4123
|
+
let metadata = context.instance?.constructor[Symbol.metadata] || metadataOrType;
|
|
4124
|
+
let isCollection = ((context.instance && typeof (context.instance['forEach']) === "function") ||
|
|
4125
|
+
(metadataOrType && typeof (metadataOrType[Symbol.metadata]) === "undefined"));
|
|
4126
|
+
if (metadata && !isCollection) {
|
|
4127
|
+
const onAdd = function (ref, prop, callback, immediate) {
|
|
4128
|
+
// immediate trigger
|
|
4129
|
+
if (immediate &&
|
|
4130
|
+
context.instance[prop] !== undefined &&
|
|
4131
|
+
!isTriggeringOnAdd // FIXME: This is a workaround (https://github.com/colyseus/schema/issues/147)
|
|
4132
|
+
) {
|
|
4133
|
+
callback(context.instance[prop], undefined);
|
|
4134
|
+
}
|
|
4135
|
+
return $root.addCallback($root.refIds.get(ref), prop, callback);
|
|
4136
|
+
};
|
|
4137
|
+
/**
|
|
4138
|
+
* Schema instances
|
|
4139
|
+
*/
|
|
4140
|
+
return new Proxy({
|
|
4141
|
+
listen: function listen(prop, callback, immediate = true) {
|
|
4142
|
+
if (context.instance) {
|
|
4143
|
+
return onAdd(context.instance, prop, callback, immediate);
|
|
4144
|
+
}
|
|
4145
|
+
else {
|
|
4146
|
+
// collection instance not received yet
|
|
4147
|
+
context.onInstanceAvailable((ref, existing) => onAdd(ref, prop, callback, immediate && existing));
|
|
4148
|
+
}
|
|
4149
|
+
},
|
|
4150
|
+
onChange: function onChange(callback) {
|
|
4151
|
+
return $root.addCallback($root.refIds.get(context.instance), OPERATION.REPLACE, callback);
|
|
4152
|
+
},
|
|
4153
|
+
bindTo: function bindTo(targetObject, properties) {
|
|
4154
|
+
//
|
|
4155
|
+
// TODO: refactor this implementation. There is room for improvement here.
|
|
4156
|
+
//
|
|
4157
|
+
if (!properties) {
|
|
4158
|
+
properties = Object.keys(metadata);
|
|
4159
|
+
}
|
|
4160
|
+
return $root.addCallback($root.refIds.get(context.instance), OPERATION.REPLACE, () => {
|
|
4161
|
+
properties.forEach((prop) => targetObject[prop] = context.instance[prop]);
|
|
4162
|
+
});
|
|
4163
|
+
}
|
|
4164
|
+
}, {
|
|
4165
|
+
get(target, prop) {
|
|
4166
|
+
if (metadata[prop]) {
|
|
4167
|
+
const instance = context.instance?.[prop];
|
|
4168
|
+
const onInstanceAvailable = ((callback) => {
|
|
4169
|
+
const unbind = $(context.instance).listen(prop, (value, _) => {
|
|
4170
|
+
callback(value, false);
|
|
4171
|
+
// FIXME: by "unbinding" the callback here,
|
|
4172
|
+
// it will not support when the server
|
|
4173
|
+
// re-instantiates the instance.
|
|
4174
|
+
//
|
|
4175
|
+
unbind?.();
|
|
4176
|
+
}, false);
|
|
4177
|
+
// has existing value
|
|
4178
|
+
if ($root.refIds.get(instance) !== undefined) {
|
|
4179
|
+
callback(instance, true);
|
|
4180
|
+
}
|
|
4181
|
+
});
|
|
4182
|
+
return getProxy(metadata[prop].type, {
|
|
4183
|
+
instance,
|
|
4184
|
+
parentInstance: context.instance,
|
|
4185
|
+
onInstanceAvailable,
|
|
4186
|
+
});
|
|
4187
|
+
}
|
|
4188
|
+
else {
|
|
4189
|
+
// accessing the function
|
|
4190
|
+
return target[prop];
|
|
4191
|
+
}
|
|
4192
|
+
},
|
|
4193
|
+
has(target, prop) { return metadata[prop] !== undefined; },
|
|
4194
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4195
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4196
|
+
});
|
|
4197
|
+
}
|
|
4198
|
+
else {
|
|
4199
|
+
/**
|
|
4200
|
+
* Collection instances
|
|
4201
|
+
*/
|
|
4202
|
+
const onAdd = function (ref, callback, immediate) {
|
|
4203
|
+
// Trigger callback on existing items
|
|
4204
|
+
if (immediate) {
|
|
4205
|
+
ref.forEach((v, k) => callback(v, k));
|
|
4206
|
+
}
|
|
4207
|
+
return $root.addCallback($root.refIds.get(ref), OPERATION.ADD, callback);
|
|
4208
|
+
};
|
|
4209
|
+
const onRemove = function (ref, callback) {
|
|
4210
|
+
return $root.addCallback($root.refIds.get(ref), OPERATION.DELETE, callback);
|
|
4211
|
+
};
|
|
4212
|
+
return new Proxy({
|
|
4213
|
+
onAdd: function (callback, immediate = true) {
|
|
4214
|
+
//
|
|
4215
|
+
// https://github.com/colyseus/schema/issues/147
|
|
4216
|
+
// If parent instance has "onAdd" registered, avoid triggering immediate callback.
|
|
4217
|
+
//
|
|
4218
|
+
// FIXME: "isTriggeringOnAdd" is a workaround. We should find a better way to handle this.
|
|
4219
|
+
//
|
|
4220
|
+
if (context.onInstanceAvailable) {
|
|
4221
|
+
// collection instance not received yet
|
|
4222
|
+
context.onInstanceAvailable((ref, existing) => onAdd(ref, callback, immediate && existing && !isTriggeringOnAdd));
|
|
4223
|
+
}
|
|
4224
|
+
else if (context.instance) {
|
|
4225
|
+
onAdd(context.instance, callback, immediate && !isTriggeringOnAdd);
|
|
4226
|
+
}
|
|
4227
|
+
},
|
|
4228
|
+
onRemove: function (callback) {
|
|
4229
|
+
if (context.onInstanceAvailable) {
|
|
4230
|
+
// collection instance not received yet
|
|
4231
|
+
context.onInstanceAvailable((ref) => onRemove(ref, callback));
|
|
4232
|
+
}
|
|
4233
|
+
else if (context.instance) {
|
|
4234
|
+
onRemove(context.instance, callback);
|
|
4235
|
+
}
|
|
4236
|
+
},
|
|
4237
|
+
}, {
|
|
4238
|
+
get(target, prop) {
|
|
4239
|
+
if (!target[prop]) {
|
|
4240
|
+
throw new Error(`Can't access '${prop}' through callback proxy. access the instance directly.`);
|
|
4241
|
+
}
|
|
4242
|
+
return target[prop];
|
|
4243
|
+
},
|
|
4244
|
+
has(target, prop) { return target[prop] !== undefined; },
|
|
4245
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4246
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4247
|
+
});
|
|
4248
|
+
}
|
|
4249
|
+
}
|
|
4250
|
+
function $(instance) {
|
|
4251
|
+
return getProxy(undefined, { instance });
|
|
4252
|
+
}
|
|
4253
|
+
return $;
|
|
4254
|
+
}
|
|
4255
|
+
|
|
4256
|
+
function getRawChangesCallback(decoder, callback) {
|
|
4257
|
+
decoder.triggerChanges = callback;
|
|
4258
|
+
}
|
|
4259
|
+
|
|
3939
4260
|
class StateView {
|
|
3940
4261
|
constructor() {
|
|
3941
4262
|
/**
|
|
@@ -3958,12 +4279,18 @@ class StateView {
|
|
|
3958
4279
|
console.warn("StateView#add(), invalid object:", obj);
|
|
3959
4280
|
return this;
|
|
3960
4281
|
}
|
|
4282
|
+
// FIXME: ArraySchema/MapSchema does not have metadata
|
|
4283
|
+
const metadata = obj.constructor[Symbol.metadata];
|
|
3961
4284
|
let changeTree = obj[$changes];
|
|
3962
4285
|
this.items.add(changeTree);
|
|
3963
4286
|
// Add children of this ChangeTree to this view
|
|
3964
|
-
changeTree.forEachChild((change,
|
|
3965
|
-
|
|
3966
|
-
|
|
4287
|
+
changeTree.forEachChild((change, index) => {
|
|
4288
|
+
// Do not ADD children that don't have the same tag
|
|
4289
|
+
if (metadata && metadata[metadata[index]].tag !== tag) {
|
|
4290
|
+
return;
|
|
4291
|
+
}
|
|
4292
|
+
this.add(change.ref, tag);
|
|
4293
|
+
});
|
|
3967
4294
|
// add parent ChangeTree's, if they are invisible to this view
|
|
3968
4295
|
// TODO: REFACTOR addParent()
|
|
3969
4296
|
this.addParent(changeTree, tag);
|
|
@@ -3990,7 +4317,6 @@ class StateView {
|
|
|
3990
4317
|
tags = this.tags.get(changeTree);
|
|
3991
4318
|
}
|
|
3992
4319
|
tags.add(tag);
|
|
3993
|
-
// console.log("BY TAG:", tag);
|
|
3994
4320
|
// Ref: add tagged properties
|
|
3995
4321
|
metadata?.[-3]?.[tag]?.forEach((index) => {
|
|
3996
4322
|
if (changeTree.getChange(index) !== OPERATION.DELETE) {
|
|
@@ -4129,5 +4455,5 @@ registerType("array", { constructor: ArraySchema });
|
|
|
4129
4455
|
registerType("set", { constructor: SetSchema });
|
|
4130
4456
|
registerType("collection", { constructor: CollectionSchema, });
|
|
4131
4457
|
|
|
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 };
|
|
4458
|
+
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
4459
|
//# sourceMappingURL=index.mjs.map
|