@colyseus/schema 3.0.0-alpha.10 → 3.0.0-alpha.12
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 +266 -10
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +265 -11
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +266 -10
- package/lib/decoder/strategy/StateCallbacks.d.ts +4 -7
- package/lib/decoder/strategy/StateCallbacks.js +6 -12
- package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
- package/lib/encoder/EncodeOperation.js +12 -0
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +1 -1
- package/lib/encoder/Encoder.js +20 -10
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.js +5 -1
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/decoder/strategy/StateCallbacks.ts +11 -20
- package/src/encoder/EncodeOperation.ts +13 -0
- package/src/encoder/Encoder.ts +22 -10
- package/src/index.ts +3 -0
package/build/esm/index.mjs
CHANGED
|
@@ -942,6 +942,18 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
|
|
|
942
942
|
}
|
|
943
943
|
const type = changeTree.getType(field);
|
|
944
944
|
const value = changeTree.getValue(field);
|
|
945
|
+
// try { throw new Error(); } catch (e) {
|
|
946
|
+
// // only print if not coming from Reflection.ts
|
|
947
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
948
|
+
// console.log("encodeKeyValueOperation -> ", {
|
|
949
|
+
// ref: changeTree.ref.constructor.name,
|
|
950
|
+
// field,
|
|
951
|
+
// operation: OPERATION[operation],
|
|
952
|
+
// value: value?.toJSON(),
|
|
953
|
+
// items: ref.toJSON(),
|
|
954
|
+
// });
|
|
955
|
+
// }
|
|
956
|
+
// }
|
|
945
957
|
// TODO: inline this function call small performance gain
|
|
946
958
|
encodeValue(encoder, bytes, ref, type, value, field, operation, it);
|
|
947
959
|
};
|
|
@@ -3381,9 +3393,8 @@ class Encoder {
|
|
|
3381
3393
|
this.state = state;
|
|
3382
3394
|
state[$changes].setRoot(this.root);
|
|
3383
3395
|
}
|
|
3384
|
-
encode(it = { offset: 0 }, view, buffer = this.sharedBuffer, changeTrees = this.root.changes) {
|
|
3396
|
+
encode(it = { offset: 0 }, view, buffer = this.sharedBuffer, changeTrees = this.root.changes, isEncodeAll = this.root.allChanges === changeTrees) {
|
|
3385
3397
|
const initialOffset = it.offset; // cache current offset in case we need to resize the buffer
|
|
3386
|
-
const isEncodeAll = this.root.allChanges === changeTrees;
|
|
3387
3398
|
const hasView = (view !== undefined);
|
|
3388
3399
|
const rootChangeTree = this.state[$changes];
|
|
3389
3400
|
const changeTreesIterator = changeTrees.entries();
|
|
@@ -3392,6 +3403,12 @@ class Encoder {
|
|
|
3392
3403
|
const ctor = ref['constructor'];
|
|
3393
3404
|
const encoder = ctor[$encoder];
|
|
3394
3405
|
const filter = ctor[$filter];
|
|
3406
|
+
// try { throw new Error(); } catch (e) {
|
|
3407
|
+
// // only print if not coming from Reflection.ts
|
|
3408
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
3409
|
+
// console.log("ChangeTree:", { ref: ref.constructor.name, });
|
|
3410
|
+
// }
|
|
3411
|
+
// }
|
|
3395
3412
|
if (hasView) {
|
|
3396
3413
|
if (!view.items.has(changeTree)) {
|
|
3397
3414
|
view.invisible.add(changeTree);
|
|
@@ -3421,11 +3438,16 @@ class Encoder {
|
|
|
3421
3438
|
// view?.invisible.add(changeTree);
|
|
3422
3439
|
continue;
|
|
3423
3440
|
}
|
|
3424
|
-
//
|
|
3425
|
-
//
|
|
3426
|
-
//
|
|
3427
|
-
//
|
|
3428
|
-
//
|
|
3441
|
+
// try { throw new Error(); } catch (e) {
|
|
3442
|
+
// // only print if not coming from Reflection.ts
|
|
3443
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
3444
|
+
// // console.log("WILL ENCODE", {
|
|
3445
|
+
// // ref: changeTree.ref.constructor.name,
|
|
3446
|
+
// // fieldIndex,
|
|
3447
|
+
// // operation: OPERATION[operation],
|
|
3448
|
+
// // });
|
|
3449
|
+
// }
|
|
3450
|
+
// }
|
|
3429
3451
|
encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
|
|
3430
3452
|
}
|
|
3431
3453
|
}
|
|
@@ -3440,7 +3462,7 @@ class Encoder {
|
|
|
3440
3462
|
if (buffer === this.sharedBuffer) {
|
|
3441
3463
|
this.sharedBuffer = buffer;
|
|
3442
3464
|
}
|
|
3443
|
-
return this.encode({ offset: initialOffset }, view, buffer);
|
|
3465
|
+
return this.encode({ offset: initialOffset }, view, buffer, changeTrees, isEncodeAll);
|
|
3444
3466
|
}
|
|
3445
3467
|
else {
|
|
3446
3468
|
//
|
|
@@ -3460,14 +3482,14 @@ class Encoder {
|
|
|
3460
3482
|
// Array.from(this.root.allChanges.entries()).map((item) => {
|
|
3461
3483
|
// console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
|
|
3462
3484
|
// });
|
|
3463
|
-
return this.encode(it, undefined, buffer, this.root.allChanges);
|
|
3485
|
+
return this.encode(it, undefined, buffer, this.root.allChanges, true);
|
|
3464
3486
|
}
|
|
3465
3487
|
encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3466
3488
|
const viewOffset = it.offset;
|
|
3467
3489
|
// console.log(`encodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
|
|
3468
3490
|
// this.debugAllFilteredChanges();
|
|
3469
3491
|
// try to encode "filtered" changes
|
|
3470
|
-
this.encode(it, view, bytes, this.root.allFilteredChanges);
|
|
3492
|
+
this.encode(it, view, bytes, this.root.allFilteredChanges, true);
|
|
3471
3493
|
return Buffer.concat([
|
|
3472
3494
|
bytes.subarray(0, sharedOffset),
|
|
3473
3495
|
bytes.subarray(viewOffset, it.offset)
|
|
@@ -3937,6 +3959,238 @@ __decorate([
|
|
|
3937
3959
|
type([ReflectionType])
|
|
3938
3960
|
], Reflection.prototype, "types", void 0);
|
|
3939
3961
|
|
|
3962
|
+
function getStateCallbacks(decoder) {
|
|
3963
|
+
const $root = decoder.root;
|
|
3964
|
+
const callbacks = $root.callbacks;
|
|
3965
|
+
let isTriggeringOnAdd = false;
|
|
3966
|
+
decoder.triggerChanges = function (allChanges) {
|
|
3967
|
+
const uniqueRefIds = new Set();
|
|
3968
|
+
for (let i = 0, l = allChanges.length; i < l; i++) {
|
|
3969
|
+
const change = allChanges[i];
|
|
3970
|
+
const refId = change.refId;
|
|
3971
|
+
const ref = change.ref;
|
|
3972
|
+
const $callbacks = callbacks[refId];
|
|
3973
|
+
if (!$callbacks) {
|
|
3974
|
+
continue;
|
|
3975
|
+
}
|
|
3976
|
+
//
|
|
3977
|
+
// trigger onRemove on child structure.
|
|
3978
|
+
//
|
|
3979
|
+
if ((change.op & OPERATION.DELETE) === OPERATION.DELETE &&
|
|
3980
|
+
change.previousValue instanceof Schema) {
|
|
3981
|
+
const deleteCallbacks = callbacks[$root.refIds.get(change.previousValue)]?.[OPERATION.DELETE];
|
|
3982
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
3983
|
+
deleteCallbacks[i]();
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
if (ref instanceof Schema) {
|
|
3987
|
+
//
|
|
3988
|
+
// Handle schema instance
|
|
3989
|
+
//
|
|
3990
|
+
if (!uniqueRefIds.has(refId)) {
|
|
3991
|
+
try {
|
|
3992
|
+
// trigger onChange
|
|
3993
|
+
const replaceCallbacks = $callbacks?.[OPERATION.REPLACE];
|
|
3994
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
3995
|
+
replaceCallbacks[i]();
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
catch (e) {
|
|
3999
|
+
console.error(e);
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
4002
|
+
try {
|
|
4003
|
+
if ($callbacks.hasOwnProperty(change.field)) {
|
|
4004
|
+
const fieldCallbacks = $callbacks[change.field];
|
|
4005
|
+
for (let i = fieldCallbacks?.length - 1; i >= 0; i--) {
|
|
4006
|
+
fieldCallbacks[i](change.value, change.previousValue);
|
|
4007
|
+
}
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
catch (e) {
|
|
4011
|
+
//
|
|
4012
|
+
console.error(e);
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
else {
|
|
4016
|
+
//
|
|
4017
|
+
// Handle collection of items
|
|
4018
|
+
//
|
|
4019
|
+
if (change.op === OPERATION.ADD && change.previousValue === undefined) {
|
|
4020
|
+
// triger onAdd
|
|
4021
|
+
isTriggeringOnAdd = true;
|
|
4022
|
+
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
4023
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4024
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4025
|
+
}
|
|
4026
|
+
isTriggeringOnAdd = false;
|
|
4027
|
+
}
|
|
4028
|
+
else if ((change.op & OPERATION.DELETE) === OPERATION.DELETE) {
|
|
4029
|
+
//
|
|
4030
|
+
// FIXME: `previousValue` should always be available.
|
|
4031
|
+
//
|
|
4032
|
+
if (change.previousValue !== undefined) {
|
|
4033
|
+
// triger onRemove
|
|
4034
|
+
const deleteCallbacks = $callbacks[OPERATION.DELETE];
|
|
4035
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
4036
|
+
deleteCallbacks[i](change.previousValue, change.dynamicIndex ?? change.field);
|
|
4037
|
+
}
|
|
4038
|
+
}
|
|
4039
|
+
// Handle DELETE_AND_ADD operations
|
|
4040
|
+
// FIXME: should we set "isTriggeringOnAdd" here?
|
|
4041
|
+
if ((change.op & OPERATION.ADD) === OPERATION.ADD) {
|
|
4042
|
+
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
4043
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4044
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
4048
|
+
// trigger onChange
|
|
4049
|
+
if (change.value !== change.previousValue) {
|
|
4050
|
+
const replaceCallbacks = $callbacks[OPERATION.REPLACE];
|
|
4051
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
4052
|
+
replaceCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
uniqueRefIds.add(refId);
|
|
4057
|
+
}
|
|
4058
|
+
};
|
|
4059
|
+
function getProxy(metadataOrType, context) {
|
|
4060
|
+
let metadata = context.instance?.constructor[Symbol.metadata] || metadataOrType;
|
|
4061
|
+
let isCollection = ((context.instance && typeof (context.instance['forEach']) === "function") ||
|
|
4062
|
+
(metadataOrType && typeof (metadataOrType[Symbol.metadata]) === "undefined"));
|
|
4063
|
+
if (metadata && !isCollection) {
|
|
4064
|
+
const onAdd = function (ref, prop, callback, immediate) {
|
|
4065
|
+
// immediate trigger
|
|
4066
|
+
if (immediate &&
|
|
4067
|
+
context.instance[prop] !== undefined &&
|
|
4068
|
+
!isTriggeringOnAdd // FIXME: This is a workaround (https://github.com/colyseus/schema/issues/147)
|
|
4069
|
+
) {
|
|
4070
|
+
callback(context.instance[prop], undefined);
|
|
4071
|
+
}
|
|
4072
|
+
return $root.addCallback($root.refIds.get(ref), prop, callback);
|
|
4073
|
+
};
|
|
4074
|
+
/**
|
|
4075
|
+
* Schema instances
|
|
4076
|
+
*/
|
|
4077
|
+
return new Proxy({
|
|
4078
|
+
listen: function listen(prop, callback, immediate = true) {
|
|
4079
|
+
if (context.instance) {
|
|
4080
|
+
return onAdd(context.instance, prop, callback, immediate);
|
|
4081
|
+
}
|
|
4082
|
+
else {
|
|
4083
|
+
// collection instance not received yet
|
|
4084
|
+
context.onInstanceAvailable((ref, existing) => onAdd(ref, prop, callback, immediate && existing));
|
|
4085
|
+
}
|
|
4086
|
+
},
|
|
4087
|
+
onChange: function onChange(callback) {
|
|
4088
|
+
return $root.addCallback($root.refIds.get(context.instance), OPERATION.REPLACE, callback);
|
|
4089
|
+
},
|
|
4090
|
+
bindTo: function bindTo(targetObject, properties) {
|
|
4091
|
+
// return $root.addCallback(
|
|
4092
|
+
// $root.refIds.get(context.instance),
|
|
4093
|
+
// OPERATION.BIND,
|
|
4094
|
+
// callback
|
|
4095
|
+
// );
|
|
4096
|
+
console.log("bindTo", targetObject, properties);
|
|
4097
|
+
}
|
|
4098
|
+
}, {
|
|
4099
|
+
get(target, prop) {
|
|
4100
|
+
if (metadata[prop]) {
|
|
4101
|
+
const instance = context.instance?.[prop];
|
|
4102
|
+
const onInstanceAvailable = ((callback) => {
|
|
4103
|
+
const unbind = $(context.instance).listen(prop, (value, _) => {
|
|
4104
|
+
callback(value, false);
|
|
4105
|
+
// FIXME: by "unbinding" the callback here,
|
|
4106
|
+
// it will not support when the server
|
|
4107
|
+
// re-instantiates the instance.
|
|
4108
|
+
//
|
|
4109
|
+
unbind?.();
|
|
4110
|
+
}, false);
|
|
4111
|
+
// has existing value
|
|
4112
|
+
if ($root.refIds.get(instance) !== undefined) {
|
|
4113
|
+
callback(instance, true);
|
|
4114
|
+
}
|
|
4115
|
+
});
|
|
4116
|
+
return getProxy(metadata[prop].type, {
|
|
4117
|
+
instance,
|
|
4118
|
+
parentInstance: context.instance,
|
|
4119
|
+
onInstanceAvailable,
|
|
4120
|
+
});
|
|
4121
|
+
}
|
|
4122
|
+
else {
|
|
4123
|
+
// accessing the function
|
|
4124
|
+
return target[prop];
|
|
4125
|
+
}
|
|
4126
|
+
},
|
|
4127
|
+
has(target, prop) { return metadata[prop] !== undefined; },
|
|
4128
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4129
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4130
|
+
});
|
|
4131
|
+
}
|
|
4132
|
+
else {
|
|
4133
|
+
/**
|
|
4134
|
+
* Collection instances
|
|
4135
|
+
*/
|
|
4136
|
+
const onAdd = function (ref, callback, immediate) {
|
|
4137
|
+
// Trigger callback on existing items
|
|
4138
|
+
if (immediate) {
|
|
4139
|
+
ref.forEach((v, k) => callback(v, k));
|
|
4140
|
+
}
|
|
4141
|
+
return $root.addCallback($root.refIds.get(ref), OPERATION.ADD, callback);
|
|
4142
|
+
};
|
|
4143
|
+
const onRemove = function (ref, callback) {
|
|
4144
|
+
return $root.addCallback($root.refIds.get(ref), OPERATION.DELETE, callback);
|
|
4145
|
+
};
|
|
4146
|
+
return new Proxy({
|
|
4147
|
+
onAdd: function (callback, immediate = true) {
|
|
4148
|
+
//
|
|
4149
|
+
// https://github.com/colyseus/schema/issues/147
|
|
4150
|
+
// If parent instance has "onAdd" registered, avoid triggering immediate callback.
|
|
4151
|
+
//
|
|
4152
|
+
// FIXME: "isTriggeringOnAdd" is a workaround. We should find a better way to handle this.
|
|
4153
|
+
//
|
|
4154
|
+
if (context.onInstanceAvailable) {
|
|
4155
|
+
// collection instance not received yet
|
|
4156
|
+
context.onInstanceAvailable((ref, existing) => onAdd(ref, callback, immediate && existing && !isTriggeringOnAdd));
|
|
4157
|
+
}
|
|
4158
|
+
else if (context.instance) {
|
|
4159
|
+
onAdd(context.instance, callback, immediate && !isTriggeringOnAdd);
|
|
4160
|
+
}
|
|
4161
|
+
},
|
|
4162
|
+
onRemove: function (callback) {
|
|
4163
|
+
if (context.onInstanceAvailable) {
|
|
4164
|
+
// collection instance not received yet
|
|
4165
|
+
context.onInstanceAvailable((ref) => onRemove(ref, callback));
|
|
4166
|
+
}
|
|
4167
|
+
else if (context.instance) {
|
|
4168
|
+
onRemove(context.instance, callback);
|
|
4169
|
+
}
|
|
4170
|
+
},
|
|
4171
|
+
}, {
|
|
4172
|
+
get(target, prop) {
|
|
4173
|
+
if (!target[prop]) {
|
|
4174
|
+
throw new Error(`Can't access '${prop}' through callback proxy. access the instance directly.`);
|
|
4175
|
+
}
|
|
4176
|
+
return target[prop];
|
|
4177
|
+
},
|
|
4178
|
+
has(target, prop) { return target[prop] !== undefined; },
|
|
4179
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4180
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4181
|
+
});
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
function $(instance) {
|
|
4185
|
+
return getProxy(undefined, { instance });
|
|
4186
|
+
}
|
|
4187
|
+
return $(decoder.state);
|
|
4188
|
+
}
|
|
4189
|
+
|
|
4190
|
+
function getRawChangesCallback(decoder, callback) {
|
|
4191
|
+
decoder.triggerChanges = callback;
|
|
4192
|
+
}
|
|
4193
|
+
|
|
3940
4194
|
class StateView {
|
|
3941
4195
|
constructor() {
|
|
3942
4196
|
/**
|
|
@@ -4130,5 +4384,5 @@ registerType("array", { constructor: ArraySchema });
|
|
|
4130
4384
|
registerType("set", { constructor: SetSchema });
|
|
4131
4385
|
registerType("collection", { constructor: CollectionSchema, });
|
|
4132
4386
|
|
|
4133
|
-
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 };
|
|
4387
|
+
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, getRawChangesCallback, getStateCallbacks, registerType, type, view };
|
|
4134
4388
|
//# sourceMappingURL=index.mjs.map
|