@colyseus/schema 3.0.0-alpha.11 → 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.
@@ -3393,9 +3393,8 @@ class Encoder {
3393
3393
  this.state = state;
3394
3394
  state[$changes].setRoot(this.root);
3395
3395
  }
3396
- 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) {
3397
3397
  const initialOffset = it.offset; // cache current offset in case we need to resize the buffer
3398
- const isEncodeAll = this.root.allChanges === changeTrees;
3399
3398
  const hasView = (view !== undefined);
3400
3399
  const rootChangeTree = this.state[$changes];
3401
3400
  const changeTreesIterator = changeTrees.entries();
@@ -3463,7 +3462,7 @@ class Encoder {
3463
3462
  if (buffer === this.sharedBuffer) {
3464
3463
  this.sharedBuffer = buffer;
3465
3464
  }
3466
- return this.encode({ offset: initialOffset }, view, buffer);
3465
+ return this.encode({ offset: initialOffset }, view, buffer, changeTrees, isEncodeAll);
3467
3466
  }
3468
3467
  else {
3469
3468
  //
@@ -3483,14 +3482,14 @@ class Encoder {
3483
3482
  // Array.from(this.root.allChanges.entries()).map((item) => {
3484
3483
  // console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
3485
3484
  // });
3486
- return this.encode(it, undefined, buffer, this.root.allChanges);
3485
+ return this.encode(it, undefined, buffer, this.root.allChanges, true);
3487
3486
  }
3488
3487
  encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
3489
3488
  const viewOffset = it.offset;
3490
3489
  // console.log(`encodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
3491
3490
  // this.debugAllFilteredChanges();
3492
3491
  // try to encode "filtered" changes
3493
- this.encode(it, view, bytes, this.root.allFilteredChanges);
3492
+ this.encode(it, view, bytes, this.root.allFilteredChanges, true);
3494
3493
  return Buffer.concat([
3495
3494
  bytes.subarray(0, sharedOffset),
3496
3495
  bytes.subarray(viewOffset, it.offset)
@@ -3960,6 +3959,238 @@ __decorate([
3960
3959
  type([ReflectionType])
3961
3960
  ], Reflection.prototype, "types", void 0);
3962
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
+
3963
4194
  class StateView {
3964
4195
  constructor() {
3965
4196
  /**
@@ -4153,5 +4384,5 @@ registerType("array", { constructor: ArraySchema });
4153
4384
  registerType("set", { constructor: SetSchema });
4154
4385
  registerType("collection", { constructor: CollectionSchema, });
4155
4386
 
4156
- 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 };
4157
4388
  //# sourceMappingURL=index.mjs.map