multyx-client 0.1.9 → 0.1.10

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.
@@ -9,9 +9,9 @@ export default class MultyxClientObject {
9
9
  private multyx;
10
10
  propertyPath: string[];
11
11
  editable: boolean;
12
- private editCallbacks;
12
+ private writeCallbacks;
13
13
  get value(): RawObject<MultyxClientList | MultyxClientObject | MultyxClientValue>;
14
- onWrite(callback: (key: any, value: any) => void): void;
14
+ onWrite(callback: (key: string, value: MultyxClientItem | undefined, oldValue: MultyxClientItem | undefined) => void): void;
15
15
  [Edit](updatePath: string[], value: any): void;
16
16
  [key: string]: any;
17
17
  constructor(multyx: Multyx, object: RawObject | EditWrapper<RawObject>, propertyPath: string[] | undefined, editable: boolean);
@@ -35,6 +35,7 @@ export default class MultyxClientObject {
35
35
  */
36
36
  [Unpack](constraints: RawObject): void;
37
37
  hydrateFromServer(value: RawObject): void;
38
- private applyServerValue;
38
+ private tryApplyServerValue;
39
39
  private notifyPropertyWaiters;
40
+ private enqueueWriteCallbacks;
40
41
  }
@@ -17,7 +17,7 @@ class MultyxClientObject {
17
17
  return parsed;
18
18
  }
19
19
  onWrite(callback) {
20
- this.editCallbacks.push(callback);
20
+ this.writeCallbacks.push(callback);
21
21
  }
22
22
  [utils_1.Edit](updatePath, value) {
23
23
  var _a;
@@ -35,7 +35,7 @@ class MultyxClientObject {
35
35
  }
36
36
  constructor(multyx, object, propertyPath = [], editable) {
37
37
  this.type = 'object';
38
- this.editCallbacks = [];
38
+ this.writeCallbacks = [];
39
39
  this.object = {};
40
40
  this.propertyPath = propertyPath;
41
41
  this.multyx = multyx;
@@ -113,19 +113,19 @@ class MultyxClientObject {
113
113
  set(property, value) {
114
114
  if (Array.isArray(property))
115
115
  return this.recursiveSet(property, value);
116
+ const oldValue = this.get(property);
116
117
  const serverSet = value instanceof utils_1.EditWrapper;
117
118
  const allowed = serverSet || this.editable;
118
119
  const incoming = (serverSet || (0, _1.IsMultyxClientItem)(value)) ? value.value : value;
119
120
  if (incoming === undefined)
120
121
  return this.delete(property, serverSet);
121
- if (serverSet && this.applyServerValue(property, incoming)) {
122
+ if (serverSet && this.tryApplyServerValue(property, incoming, oldValue)) {
122
123
  return true;
123
124
  }
124
125
  // Only create new MultyxClientItem when needed
125
126
  if (this.object[property] instanceof value_1.default && (typeof incoming !== 'object' || incoming === null)) {
126
127
  const bool = this.object[property].set(serverSet ? new utils_1.EditWrapper(incoming) : incoming);
127
- if (serverSet)
128
- this.editCallbacks.forEach(callback => callback(property, this.object[property]));
128
+ this.enqueueWriteCallbacks(property, oldValue);
129
129
  return bool;
130
130
  }
131
131
  // Attempting to edit property not editable to client
@@ -137,12 +137,13 @@ class MultyxClientObject {
137
137
  }
138
138
  // Creating a new value
139
139
  this.object[property] = new ((0, router_1.default)(incoming))(this.multyx, serverSet ? new utils_1.EditWrapper(incoming) : incoming, [...this.propertyPath, property], this.editable);
140
- if (serverSet)
141
- this.editCallbacks.forEach(callback => callback(property, this.object[property]));
142
140
  this.notifyPropertyWaiters(property);
141
+ this.enqueueWriteCallbacks(property, oldValue);
143
142
  return true;
144
143
  }
145
144
  delete(property, native = false) {
145
+ const key = typeof property === 'string' ? property : String(property);
146
+ const oldValue = this.get(key);
146
147
  // Attempting to edit property not editable by client
147
148
  if (!this.editable && !native) {
148
149
  if (this.multyx.options.verbose) {
@@ -151,7 +152,9 @@ class MultyxClientObject {
151
152
  return false;
152
153
  }
153
154
  delete this.object[property];
154
- this.editCallbacks.forEach(callback => callback(property, undefined));
155
+ for (const listener of this.writeCallbacks) {
156
+ this.multyx[utils_1.Add](() => listener(key, undefined, oldValue));
157
+ }
155
158
  if (!native) {
156
159
  this.multyx.ws.send(message_1.Message.Native({
157
160
  instruction: 'edit',
@@ -207,24 +210,24 @@ class MultyxClientObject {
207
210
  this.delete(key, true);
208
211
  }
209
212
  }
210
- applyServerValue(property, incoming) {
213
+ tryApplyServerValue(property, incoming, oldValue) {
211
214
  const current = this.object[property];
212
215
  if (!current)
213
216
  return false;
214
217
  if (current instanceof value_1.default && (typeof incoming !== 'object' || incoming === null)) {
215
218
  current.set(new utils_1.EditWrapper(incoming));
216
- this.editCallbacks.forEach(callback => callback(property, current));
219
+ this.enqueueWriteCallbacks(property, oldValue);
217
220
  return true;
218
221
  }
219
222
  const canHydrate = typeof (current === null || current === void 0 ? void 0 : current.hydrateFromServer) === 'function';
220
223
  if (Array.isArray(incoming) && canHydrate && current.type === 'list') {
221
224
  current.hydrateFromServer(incoming);
222
- this.editCallbacks.forEach(callback => callback(property, current));
225
+ this.enqueueWriteCallbacks(property, oldValue);
223
226
  return true;
224
227
  }
225
228
  if (isPlainObject(incoming) && canHydrate && current.type === 'object') {
226
229
  current.hydrateFromServer(incoming);
227
- this.editCallbacks.forEach(callback => callback(property, current));
230
+ this.enqueueWriteCallbacks(property, oldValue);
228
231
  return true;
229
232
  }
230
233
  return false;
@@ -236,5 +239,10 @@ class MultyxClientObject {
236
239
  this.multyx[utils_1.Done].push(...((_b = (_a = this.multyx.events.get(propSymbol)) === null || _a === void 0 ? void 0 : _a.map(e => () => e(this.object[property]))) !== null && _b !== void 0 ? _b : []));
237
240
  }
238
241
  }
242
+ enqueueWriteCallbacks(property, oldValue) {
243
+ for (const listener of this.writeCallbacks) {
244
+ this.multyx[utils_1.Add](() => listener(property, this.get(property), oldValue));
245
+ }
246
+ }
239
247
  }
240
248
  exports.default = MultyxClientObject;
package/multyx.js CHANGED
@@ -1 +1 @@
1
- !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Multyx=e():t.Multyx=e()}(self,()=>(()=>{"use strict";var t={249:function(t,e,i){var s,n=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const o=i(625),r=i(703),l=n(i(416)),a=i(449);class h{addEditCallback(t){this.editCallbacks.push(t)}get value(){var t;const e=[];for(let i=0;i<this.length;i++)e[i]=null===(t=this.get(i))||void 0===t?void 0:t.value;return e}get length(){return this.list.length}set length(t){this.list.length=t}handleShiftOperation(t,e){const i=t>=0?e>=0?"right":"left":0==e?"reverse":e<0?"length":"unknown";switch(i){case"reverse":for(let t=0;t<Math.floor(this.length/2);t++){const e=this.list[t];this.list[t]=this.list[this.length-1-t],this.list[this.length-1-t]=e}break;case"left":for(let i=t;i<this.length;i++)i+e<0||(this.list[i+e]=this.list[i]);break;case"right":for(let i=this.length-1;i>=t;i--)this.list[i+e]=this.list[i];break;case"length":this.length+=e;break;default:this.multyx.options.verbose&&console.error("Unknown shift operation: "+i)}}constructor(t,e,i=[],n){this.type="list",this.editCallbacks=[],this.toString=()=>this.value.toString(),this.valueOf=()=>this.value,this[s]=()=>this.value,this.list=[],this.propertyPath=i,this.multyx=t,this.editable=n;const o=e instanceof r.EditWrapper;e instanceof h&&(e=e.value),e instanceof r.EditWrapper&&(e=e.value);for(let t=0;t<e.length;t++)this.set(t,o?new r.EditWrapper(e[t]):e[t]);return new Proxy(this,{has:(t,e)=>"number"==typeof e?t.has(e):e in t,get:(t,e)=>e in t?t[e]:(isNaN(parseInt(e))||(e=parseInt(e)),t.get(e)),set:(t,e,i)=>e in t?(t[e]=i,!0):!!t.set(e,i),deleteProperty:(t,e)=>"number"==typeof e&&t.delete(e)})}has(t){return t>=0&&t<this.length}get(t){if("number"==typeof t)return this.list[t];if(0==t.length)return this;if(1==t.length)return this.list[parseInt(t[0])];const e=this.list[parseInt(t[0])];return!e||e instanceof o.MultyxClientValue?void 0:e.get(t.slice(1))}recursiveSet(t,e){if(0==t.length)return this.multyx.options.verbose&&console.error(`Attempting to edit MultyxClientList with no path. Setting '${this.propertyPath.join(".")}' to ${e}`),!1;if("shift"==t[0]&&e instanceof r.EditWrapper)return this.handleShiftOperation(parseInt(t[1]),e.value),!0;if(1==t.length)return this.set(parseInt(t[0]),e);let i=this.get(parseInt(t[0]));return(i instanceof o.MultyxClientValue||null==i)&&(this.set(parseInt(t[0]),new r.EditWrapper({})),i=this.get(parseInt(t[0]))),!(!i||i instanceof o.MultyxClientValue)&&i.set(t.slice(1),e)}set(t,e){if(Array.isArray(t))return this.recursiveSet(t,e);const i=this.get(t),s=e instanceof r.EditWrapper,n=s||this.editable,a=s||(0,o.IsMultyxClientItem)(e)?e.value:e;if(void 0===a)return this.delete(t,s);if(s&&this.tryApplyServerValue(t,a,i))return!0;if(this.list[t]instanceof o.MultyxClientValue&&("object"!=typeof a||null===a)){const e=this.list[t].set(s?new r.EditWrapper(a):a);return this.enqueueEditCallbacks(t,i),e}return n?(this.list[t]=new((0,l.default)(a))(this.multyx,s?new r.EditWrapper(a):a,[...this.propertyPath,t.toString()],this.editable),this.notifyIndexWaiters(t),this.enqueueEditCallbacks(t,i),!0):(this.multyx.options.verbose&&console.error(`Attempting to set property that is not editable. Setting '${this.propertyPath.join(".")+"."+t}' to ${a}`),!1)}delete(t,e=!1){const i=this.get(t);if("string"==typeof t&&(t=parseInt(t)),!this.editable&&!e)return this.multyx.options.verbose&&console.error(`Attempting to delete property that is not editable. Deleting '${this.propertyPath.join(".")+"."+t}'`),!1;delete this.list[t];for(const e of this.editCallbacks)this.multyx[r.Add](()=>e(t,void 0,i));return e||this.multyx.ws.send(a.Message.Native({instruction:"edit",path:[...this.propertyPath,t.toString()],value:void 0})),!0}await(t){if(this.has(t))return Promise.resolve(this.get(t));const e=Symbol.for("_"+this.propertyPath.join(".")+"."+t);return new Promise(t=>this.multyx.on(e,t))}push(...t){for(const e of t)this.set(this.length,e);return this.length}pop(){if(0===this.length)return;const t=this.get(this.length);return this.delete(this.length),t}unshift(...t){for(let e=this.length-1;e>=0;e--)e>=t.length?this.set(e,this.get(e-t.length)):this.set(e,t[e]);return this.length}shift(){if(0==this.length)return;this.length--;const t=this.get(0);for(let t=0;t<this.length;t++)this.set(t,this.get(t+1));return t}slice(t,e){return this.list.slice(t,e)}splice(t,e,...i){return this.list.splice(t,null!=e?e:0,...i)}setSplice(t,e,...i){void 0===e&&(e=this.length-t);let s=i.length-e;if(s>0)for(let i=this.length-1;i>=t+e;i--)this.set(i+s,this.get(i));else if(s<0){for(let i=t+e;i<this.length;i++)this.set(i+s,this.get(i));const i=this.length;for(let t=i+s;t<i;t++)this.set(t,void 0)}for(let e=t;e<i.length;e++)this.set(e,i[e])}filter(t){return this.list.filter((e,i)=>t(e,i,this))}setFilter(t){const e=[];for(let i=0;i<this.length;i++)e.push(t(this.get(i),i,this));let i=0;for(let t=0;t<e.length;t++)e[t]&&i&&this.set(t-i,this.get(t)),e[t]||i--}map(t){const e=[];for(let i=0;i<this.length;i++)e.push(t(this.get(i),i,this));return e}flat(){return this.list.flat()}setFlat(){for(let t=0;t<this.length;t++){const e=this.get(t);if(e instanceof h)for(let i=0;i<e.length;i++)t++,this.set(t,e[i])}}reduce(t,e){for(let i=0;i<this.length;i++)e=t(e,this.get(i),i,this);return e}reduceRight(t,e){for(let i=this.length-1;i>=0;i--)e=t(e,this.get(i),i,this);return e}reverse(){let t=this.length-1;for(let e=0;e<t;e++){const i=this.get(e),s=this.get(t);this.set(e,s),this.set(t,i)}return this}forEach(t){for(let e=0;e<this.length;e++)t(this.get(e),e,this)}every(t){for(let e=0;e<this.length;e++)if(!t(this.get(e),e,this))return!1;return!0}some(t){for(let e=0;e<this.length;e++)if(t(this.get(e),e,this))return!0;return!1}find(t){for(let e=0;e<this.length;e++)if(t(this.get(e),e,this))return this.get(e)}findIndex(t){for(let e=0;e<this.length;e++)if(t(this.get(e),e,this))return e;return-1}entries(){const t=[];for(let e=0;e<this.length;e++)t.push([this.get(e),e]);return t}keys(){return Array(this.length).fill(0).map((t,e)=>e)}[r.Edit](){}[r.Unpack](t){var e;for(let i=0;i<this.length;i++)null===(e=this.get(i))||void 0===e||e[r.Unpack](t[i])}[Symbol.iterator](){const t=[];for(let e=0;e<this.length;e++)t[e]=this.get(e);return t[Symbol.iterator]()}hydrateFromServer(t){if(Array.isArray(t)){for(let e=0;e<t.length;e++)this.set(e,new r.EditWrapper(t[e]));for(let e=t.length;e<this.length;e++)this.delete(e,!0);this.length=t.length}}tryApplyServerValue(t,e,i){const s=this.list[t];if(!s)return!1;if(s instanceof o.MultyxClientValue&&("object"!=typeof e||null===e))return s.set(new r.EditWrapper(e)),this.enqueueEditCallbacks(t,i),!0;const n="function"==typeof(null==s?void 0:s.hydrateFromServer);return Array.isArray(e)&&n&&"list"===s.type?(s.hydrateFromServer(e),this.enqueueEditCallbacks(t,i),!0):!(null===(l=e)||"object"!=typeof l||Array.isArray(l)||!n||"object"!==s.type||(s.hydrateFromServer(e),this.enqueueEditCallbacks(t,i),0));var l}notifyIndexWaiters(t){var e,i;const s=Symbol.for("_"+this.propertyPath.join(".")+"."+t);this.multyx.events.has(s)&&this.multyx[r.Done].push(...null!==(i=null===(e=this.multyx.events.get(s))||void 0===e?void 0:e.map(e=>()=>e(this.list[t])))&&void 0!==i?i:[])}enqueueEditCallbacks(t,e){for(const i of this.editCallbacks)this.multyx[r.Add](()=>i(t,this.get(t),e))}}s=Symbol.toPrimitive,e.default=h},280:(t,e,i)=>{var s;Object.defineProperty(e,"__esModule",{value:!0});const n=i(449),o=i(703);class r{get value(){return this.readModifiers.reduce((t,e)=>e(t),this._value)}set value(t){this._value=t,this.captureSample(t)}addReadModifier(t){this.readModifiers.push(t)}addEditCallback(t){this.editCallbacks.push(t)}[o.Edit](t,e){0==t.length&&this.set(new o.EditWrapper(e))}constructor(t,e,i=[],n){var r,l;this.readModifiers=[],this.editCallbacks=[],this.interpolationFrameMs=250,this.toString=()=>this.value.toString(),this.valueOf=()=>this.value,this[s]=()=>this.value,this.propertyPath=i,this.editable=n,this.multyx=t,this.constraints={},this.set(e);const a=Symbol.for("_"+this.propertyPath.join("."));this.multyx.events.has(a)&&this.multyx[o.Done].push(...null!==(l=null===(r=this.multyx.events.get(a))||void 0===r?void 0:r.map(t=>()=>t(this.value)))&&void 0!==l?l:[])}set(t){if(t instanceof o.EditWrapper){const e=this.value;return this.value=t.value,this.editCallbacks.forEach(i=>i(t.value,e)),!0}if(!this.editable)return this.multyx.options.verbose&&console.error(`Attempting to set property that is not editable. Setting '${this.propertyPath.join(".")}' to ${t}`),!1;let e=t;for(const i in this.constraints)if(e=(0,this.constraints[i])(e),null===e)return this.multyx.options.verbose&&console.error(`Attempting to set property that failed on constraint. Setting '${this.propertyPath.join(".")}' to ${t}, stopped by constraint '${i}'`),!1;return this._value===e?(this.value=e,!0):(this.value=e,this.multyx.ws.send(n.Message.Native({instruction:"edit",path:this.propertyPath,value:e})),!0)}bindElement(t){this.addEditCallback((e,i)=>{e!==i&&(t.innerText=e.toString())})}[o.Unpack](t){for(const[e,i]of Object.entries(t)){const t=(0,o.BuildConstraint)(e,i);t&&(this.constraints[e]=t)}}Lerp(t=250){return this.applyInterpolation("lerp",t)}PredictiveLerp(t=250){return this.applyInterpolation("predictive",t)}applyInterpolation(t,e){if("number"!=typeof this._value||Number.isNaN(this._value))throw new Error(`MultyxClientValue.${"lerp"===t?"Lerp":"PredictiveLerp"} can only be applied to numeric values`);return this.interpolationFrameMs=Math.max(1,e),this.attachInterpolationModifier(t),this}attachInterpolationModifier(t){this.interpolationModifier&&(this.readModifiers=this.readModifiers.filter(t=>t!==this.interpolationModifier)),this.interpolationModifier=e=>this.interpolateValue(e,t),this.readModifiers.push(this.interpolationModifier)}captureSample(t){if("number"!=typeof t||Number.isNaN(t))return this.latestSample=void 0,void(this.previousSample=void 0);const e=Date.now();this.latestSample?(this.previousSample=Object.assign({},this.latestSample),this.latestSample={value:t,time:e}):this.latestSample={value:t,time:e}}interpolateValue(t,e){if("number"!=typeof t||!this.latestSample||!this.previousSample)return t;const i=this.latestSample.time-this.previousSample.time;if(i<=0)return t;const s=Math.max(1,Math.min(i,this.interpolationFrameMs)),n=Math.max(0,Math.min(Date.now()-this.latestSample.time,s)),o=0===s?1:n/s,r=this.latestSample.value-this.previousSample.value;return"predictive"===e?this.latestSample.value+r*o:this.previousSample.value+r*o}}s=Symbol.toPrimitive,e.default=r},416:(t,e,i)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(t){return Array.isArray(t)?i(249).default:"object"==typeof t?i(922).default:i(280).default}},449:(t,e)=>{function i(t){let e,i;if("edit"==t.instruction?(e=0,i=[t.path.join("."),JSON.stringify(t.value)]):"input"==t.instruction?(e=1,i=[t.input,JSON.stringify(t.data)]):"resp"==t.instruction&&(e=2,i=[t.name,JSON.stringify(t.response)]),!i||void 0===e)return"";let s=e.toString();for(let t=0;t<i.length;t++)s+=i[t].replace(/;/g,";_"),t<i.length-1&&(s+=";,");return JSON.stringify([s])}Object.defineProperty(e,"__esModule",{value:!0}),e.Message=void 0,e.UncompressUpdate=function(t){const[e,...i]=t.split(/;,/),s=e[0],n=e.slice(1).replace(/;_/g,";"),o=i.map(t=>t.replace(/;_/g,";")).map(t=>"undefined"==t?void 0:JSON.parse(t));return"0"==s?{instruction:"edit",team:!1,path:n.split("."),value:o[0]}:"1"==s?{instruction:"edit",team:!0,path:n.split("."),value:o[0]}:"2"==s?{instruction:"self",property:"controller",data:JSON.parse(n)}:"3"==s?{instruction:"self",property:"uuid",data:JSON.parse(n)}:"4"==s?{instruction:"self",property:"constraint",data:JSON.parse(n)}:"9"==s?{instruction:"self",property:"space",data:JSON.parse(n)}:"5"==s?{instruction:"resp",name:n,response:o[0]}:"6"==s?{instruction:"conn",uuid:n,data:o[0]}:"7"==s?{instruction:"dcon",client:n}:"8"==s?{instruction:"init",client:JSON.parse(n),tps:o[0],constraintTable:o[1],clients:o[2],teams:o[3],space:o[4]}:void 0},e.CompressUpdate=i;class s{constructor(t,e,i=!1){this.name=t,this.data=e,this.time=Date.now(),this.native=i}static BundleOperations(t,e){return Array.isArray(e)||(e=[e]),JSON.stringify(new s("_",{operations:e,deltaTime:t}))}static Native(t){return i(t)}static Parse(t){var e,i;const n=JSON.parse(t);return Array.isArray(n)?new s("_",n,!0):new s(null!==(e=n.name)&&void 0!==e?e:"",null!==(i=n.data)&&void 0!==i?i:"",!1)}static Create(t,e){if(0==t.length)throw new Error("Multyx message cannot have empty name");if("_"==t[0]&&(t="_"+t),"function"==typeof e)throw new Error("Multyx data must be JSON storable");return JSON.stringify(new s(t,e))}}e.Message=s},625:function(t,e,i){var s=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0}),e.MultyxClientValue=e.MultyxClientObject=e.MultyxClientList=void 0,e.IsMultyxClientItem=function(t){return t instanceof n.default||t instanceof o.default||t instanceof r.default};const n=s(i(249));e.MultyxClientList=n.default;const o=s(i(922));e.MultyxClientObject=o.default;const r=s(i(280));e.MultyxClientValue=r.default},703:(t,e)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.EditWrapper=e.Edit=e.Add=e.Done=e.Unpack=void 0,e.Interpolate=function(t,e,i){if(!Array.isArray(i)||0===i.length)throw new Error("Interpolation curve must contain at least one slice");const s=[...i].sort((t,e)=>t.time-e.time),n=s[s.length-1].time,o=n<=1;let r={value:t[e],time:Date.now()},l={value:t[e],time:Date.now()};Object.defineProperty(t,e,{configurable:!0,enumerable:!0,get:()=>{if(r.time===l.time)return l.value;const t=Date.now(),e=Math.max(l.time-r.time,1),i=Math.max(0,t-l.time),a=(h=o?i/e:i,u=0,p=n,Math.min(Math.max(h,u),p));var h,u,p;let c,d=s[0],f=s[s.length-1];for(const t of s){if(!(t.time<=a)){f=t;break}d=t}if(f.time===d.time)c=d.progress;else{const t=(a-d.time)/(f.time-d.time);c=d.progress+t*(f.progress-d.progress)}return Number.isNaN(c)?r.value:"number"==typeof r.value&&"number"==typeof l.value?l.value*c+r.value*(1-c):c>=1?l.value:r.value},set:t=>{const e=Date.now();return e-l.time<10?(l.value=t,l.time=e,!0):(r=Object.assign({},l),l={value:t,time:e},!0)}})},e.BuildConstraint=function(t,e){return"min"==t?t=>t>=e[0]?t:e[0]:"max"==t?t=>t<=e[0]?t:e[0]:"int"==t?t=>Math.floor(t):"ban"==t?t=>e.includes(t)?null:t:"disabled"==t?t=>e[0]?null:t:t=>t},e.Unpack=Symbol("unpack"),e.Done=Symbol("done"),e.Add=Symbol("add"),e.Edit=Symbol("edit"),e.EditWrapper=class{constructor(t){this.value=t}}},832:(t,e,i)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.Controller=void 0;const s=i(449);e.Controller=class{constructor(t){this.listening=new Set,this.ws=t,this.preventDefault=!1,this.keys={},this.mouse={x:NaN,y:NaN,down:!1,centerX:0,centerY:0,scaleX:1,scaleY:1},document.addEventListener("keydown",t=>{this.preventDefault&&t.preventDefault();const e=t.key.toLowerCase();this.keys[e]&&this.listening.has("keyhold")&&this.relayInput("keyhold",{code:e}),this.keys[t.code]&&this.listening.has("keyhold")&&this.relayInput("keyhold",{code:t.code}),this.listening.has(e)&&!this.keys[e]&&this.relayInput("keydown",{code:t.key}),this.listening.has(t.code)&&!this.keys[t.code]&&this.relayInput("keydown",{code:t.code}),this.keys[e]=!0,this.keys[t.code]=!0}),document.addEventListener("keyup",t=>{this.preventDefault&&t.preventDefault();const e=t.key.toLowerCase();delete this.keys[e],delete this.keys[t.code],this.listening.has(e)&&this.relayInput("keyup",{code:e}),this.listening.has(t.code)&&this.relayInput("keyup",{code:t.code})}),document.addEventListener("mousedown",t=>{if(this.preventDefault&&t.preventDefault(),this.mouseGetter){const t=this.mouseGetter();this.mouse.x=t.x,this.mouse.y=t.y}else this.mouse.x=(t.clientX-this.mouse.centerX)/this.mouse.scaleX,this.mouse.y=(t.clientY-this.mouse.centerY)/this.mouse.scaleY;this.mouse.down=!0,this.listening.has("mousedown")&&this.relayInput("mousedown",{x:this.mouse.x,y:this.mouse.y})}),document.addEventListener("mouseup",t=>{if(this.preventDefault&&t.preventDefault(),this.mouseGetter){const t=this.mouseGetter();this.mouse.x=t.x,this.mouse.y=t.y}else this.mouse.x=(t.clientX-this.mouse.centerX)/this.mouse.scaleX,this.mouse.y=(t.clientY-this.mouse.centerY)/this.mouse.scaleY;this.mouse.down=!1,this.listening.has("mouseup")&&this.relayInput("mouseup",{x:this.mouse.x,y:this.mouse.y})}),document.addEventListener("mousemove",t=>{if(this.preventDefault&&t.preventDefault(),this.mouseGetter){const t=this.mouseGetter();this.mouse.x=t.x,this.mouse.y=t.y}else this.mouse.x=(t.clientX-this.mouse.centerX)/this.mouse.scaleX,this.mouse.y=(t.clientY-this.mouse.centerY)/this.mouse.scaleY;this.listening.has("mousemove")&&this.relayInput("mousemove",{x:this.mouse.x,y:this.mouse.y})})}mapCanvasPosition(t,e){const i="top"in e,s="bottom"in e,n="left"in e,o="right"in e,r=e.anchor,l=t.getBoundingClientRect(),a=(t,...e)=>{const i=t?"Cannot include value for ":"Must include value for ",s=1==e.length?e[0]:e.slice(0,-1).join(", ")+(t?" and ":" or ")+e.slice(-1)[0],n=r?" if anchoring at "+r:" if not anchoring";console.error(i+s+n)},h=l.width/l.height,u=l.height/l.width;if((Number.isNaN(h)||Number.isNaN(u))&&console.error("Canvas element bounding box is flat, canvas must be present on the screen"),r){if("center"==r){if(i&&s&&e.top!==-e.bottom||n&&o&&e.left!==-e.right)return a(!0,"top","bottom","left","right");i?(e.left=n?e.left:o?-e.right:-Math.abs(h*e.top),e.right=n?-e.left:o?e.right:Math.abs(h*e.top),e.bottom=-e.top):s?(e.left=n?e.left:o?-e.right:-Math.abs(h*e.bottom),e.right=n?-e.left:o?e.right:Math.abs(h*e.bottom),e.top=-e.bottom):n?(e.top=i?e.top:s?-e.bottom:-Math.abs(u*e.left),e.bottom=i?-e.top:s?e.bottom:Math.abs(u*e.left),e.right=-e.left):o&&(e.top=i?e.top:s?-e.bottom:-Math.abs(u*e.right),e.bottom=i?-e.top:s?e.bottom:Math.abs(u*e.right),e.left=-e.right)}else if("bottom"==r){if(!n&&!o&&!i)return a(!1,"left","right","top");if(e.bottom)return a(!0,"bottom");e.bottom=0,n?(e.top=Math.abs(u*e.left*2),e.right=-e.left):o?(e.top=Math.abs(u*e.right*2),e.left=-e.right):(e.left=-Math.abs(h*e.top/2),e.right=-e.left)}else if("top"==r){if(!n&&!o&&!s)return a(!1,"left","right","bottom");if(e.top)return a(!0,"top");e.top=0,n?(e.bottom=Math.abs(u*e.left*2),e.right=-e.left):o?(e.bottom=Math.abs(u*e.right*2),e.left=-e.right):(e.left=-Math.abs(h*e.bottom/2),e.right=-e.left)}else if("left"==r){if(!i&&!s&&!o)return a(!1,"top","bottom","right");if(n)return a(!0,"left");e.left=0,i?(e.right=-Math.abs(h*e.top*2),e.bottom=-e.top):s?(e.right=Math.abs(h*e.bottom*2),e.top=-e.bottom):(e.top=-Math.abs(u*e.right/2),e.bottom=-e.top)}else if("right"==r){if(!i&&!s&&!n)return a(!1,"top","bottom","left");if(o)return a(!0,"right");e.right=0,i?(e.left=-Math.abs(h*e.top*2),e.bottom=-e.top):s?(e.left=Math.abs(h*e.bottom*2),e.top=-e.bottom):(e.top=-Math.abs(u*e.right/2),e.bottom=-e.top)}else if("topleft"==r){if(!o&&!s)return a(!1,"right","bottom");if(n||i)return a(!0,"left","top");e.left=e.top=0,o?e.bottom=Math.abs(u*e.right):e.right=Math.abs(h*e.bottom)}else if("topright"==r){if(!n&&!s)return a(!1,"left","bottom");if(o||i)return a(!0,"right","top");e.right=e.top=0,n?e.bottom=Math.abs(u*e.left):e.left=Math.abs(h*e.bottom)}else if("bottomleft"==r){if(!o&&!i)return a(!1,"right","top");if(s||n)return a(!0,"bottom","left");e.left=e.bottom=0,o?e.top=Math.abs(u*e.right):e.right=Math.abs(h*e.top)}else if("bottomright"==r){if(!i&&!n)return a(!1,"top","left");if(o||s)return a(!0,"bottom","right");e.right=e.bottom=0,n?e.top=Math.abs(u*e.left):e.left=Math.abs(h*e.top)}}else{if(!i&&!s)return a(!1,"top","bottom");if(s?i||(e.top=e.bottom-t.height):e.bottom=e.top+t.height,!n&&!o)return a(!1,"left","right");o?n||(e.left=e.right-t.width):e.right=e.left+t.width}const p=t.getContext("2d");null==p||p.setTransform(1,0,0,1,0,0),t.width=Math.floor(Math.abs(e.right-e.left)),t.height=Math.floor(Math.abs(e.bottom-e.top)),e.right<e.left&&(null==p||p.scale(-1,1)),e.top>e.bottom&&(null==p||p.scale(1,-1)),null==p||p.translate(-e.left,-e.top)}mapMousePosition(t,e,i=document.body,s=1,n=s){const o=window.innerWidth/(i instanceof HTMLCanvasElement?i.width:i.clientWidth),r=window.innerHeight/(i instanceof HTMLCanvasElement?i.height:i.clientHeight),l=i.getBoundingClientRect();this.mouse.centerX=l.left+t*o,this.mouse.centerY=l.top+e*r,this.mouse.scaleX=s*o,this.mouse.scaleY=n*r}mapMouseToCanvas(t){const e=t.getContext("2d"),i=null==e?void 0:e.getTransform(),s=t.getBoundingClientRect(),n=s.width/t.width,o=s.height/t.height;this.mouse.centerX=s.left+(null==i?void 0:i.e)*n,this.mouse.centerY=s.top+(null==i?void 0:i.f)*o,this.mouse.scaleX=n*(null==i?void 0:i.a),this.mouse.scaleY=o*(null==i?void 0:i.d)}setMouseAs(t){this.mouseGetter=t}relayInput(t,e){if(1!==this.ws.readyState)throw new Error("Websocket connection is "+(2==this.ws.readyState?"closing":"closed"));this.ws.send(s.Message.Native(Object.assign({instruction:"input",input:t},e?{data:e}:{})))}}},922:function(t,e,i){var s=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const n=i(449),o=i(703),r=i(625),l=s(i(416)),a=s(i(280)),h=t=>null!==t&&"object"==typeof t&&!Array.isArray(t);class u{get value(){const t={};for(const e in this.object)t[e]=this.object[e];return t}onWrite(t){this.editCallbacks.push(t)}[o.Edit](t,e){var i;1!=t.length?(0==t.length&&this.multyx.options.verbose&&console.error("Update path is empty. Attempting to edit MultyxClientObject with no path."),this.has(t[0])||this.set(t[0],new o.EditWrapper({})),null===(i=this.get(t[0]))||void 0===i||i[o.Edit](t.slice(1),e)):this.set(t[0],new o.EditWrapper(e))}constructor(t,e,i=[],s){this.type="object",this.editCallbacks=[],this.object={},this.propertyPath=i,this.multyx=t,this.editable=s;const n=e instanceof o.EditWrapper;e instanceof u&&(e=e.value),e instanceof o.EditWrapper&&(e=e.value);for(const t in e)this.set(t,n?new o.EditWrapper(e[t]):e[t]);if(this.constructor===u)return new Proxy(this,{has:(t,e)=>t.has(e),get:(t,e)=>e in t?t[e]:t.get(e),set:(t,e,i)=>e in t?(t[e]=i,!0):t.set(e,i),deleteProperty:(t,e)=>t.delete(e,!1)})}has(t){return t in this.object}get(t){if("string"==typeof t)return this.object[t];if(0==t.length)return this;if(1==t.length)return this.object[t[0]];const e=this.object[t[0]];return!e||e instanceof a.default?void 0:e.get(t.slice(1))}recursiveSet(t,e){if(0==t.length)return this.multyx.options.verbose&&console.error(`Attempting to edit MultyxClientObject with no path. Setting '${this.propertyPath.join(".")}' to ${e}`),!1;if(1==t.length)return this.set(t[0],e);let i=this.get(t[0]);return(i instanceof a.default||null==i)&&(isNaN(parseInt(t[1]))?(this.set(t[0],new o.EditWrapper({})),i=this.get(t[0])):(this.set(t[0],new o.EditWrapper([])),i=this.get(t[0]))),i.set(t.slice(1),e)}set(t,e){if(Array.isArray(t))return this.recursiveSet(t,e);const i=e instanceof o.EditWrapper,s=i||this.editable,n=i||(0,r.IsMultyxClientItem)(e)?e.value:e;if(void 0===n)return this.delete(t,i);if(i&&this.applyServerValue(t,n))return!0;if(this.object[t]instanceof a.default&&("object"!=typeof n||null===n)){const e=this.object[t].set(i?new o.EditWrapper(n):n);return i&&this.editCallbacks.forEach(e=>e(t,this.object[t])),e}return s?(this.object[t]=new((0,l.default)(n))(this.multyx,i?new o.EditWrapper(n):n,[...this.propertyPath,t],this.editable),i&&this.editCallbacks.forEach(e=>e(t,this.object[t])),this.notifyPropertyWaiters(t),!0):(this.multyx.options.verbose&&console.error(`Attempting to set property that is not editable. Setting '${this.propertyPath.join(".")+"."+t}' to ${n}`),!1)}delete(t,e=!1){return this.editable||e?(delete this.object[t],this.editCallbacks.forEach(e=>e(t,void 0)),e||this.multyx.ws.send(n.Message.Native({instruction:"edit",path:[...this.propertyPath,t],value:void 0})),!0):(this.multyx.options.verbose&&console.error(`Attempting to delete property that is not editable. Deleting '${this.propertyPath.join(".")+"."+t}'`),!1)}keys(){return Object.keys(this.object)}values(){return Object.values(this.object)}entries(){const t=[];for(let e in this.object)t.push([e,this.get(e)]);return t}await(t){if(this.has(t))return Promise.resolve(this.get(t));const e=Symbol.for("_"+this.propertyPath.join(".")+"."+t);return new Promise(t=>this.multyx.on(e,t))}[o.Unpack](t){var e;for(const i in t)null===(e=this.object[i])||void 0===e||e[o.Unpack](t[i])}hydrateFromServer(t){if(!h(t))return;const e=new Set(Object.keys(this.object));for(const[i,s]of Object.entries(t))e.delete(i),this.set(i,new o.EditWrapper(s));for(const t of e)this.delete(t,!0)}applyServerValue(t,e){const i=this.object[t];if(!i)return!1;if(i instanceof a.default&&("object"!=typeof e||null===e))return i.set(new o.EditWrapper(e)),this.editCallbacks.forEach(e=>e(t,i)),!0;const s="function"==typeof(null==i?void 0:i.hydrateFromServer);return(Array.isArray(e)&&s&&"list"===i.type||!(!h(e)||!s||"object"!==i.type))&&(i.hydrateFromServer(e),this.editCallbacks.forEach(e=>e(t,i)),!0)}notifyPropertyWaiters(t){var e,i;const s=Symbol.for("_"+this.propertyPath.join(".")+"."+t);this.multyx.events.has(s)&&this.multyx[o.Done].push(...null!==(i=null===(e=this.multyx.events.get(s))||void 0===e?void 0:e.map(e=>()=>e(this.object[t])))&&void 0!==i?i:[])}}e.default=u},960:(t,e)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DefaultOptions=void 0,e.DefaultOptions={port:8443,secure:!1,uri:"localhost",verbose:!1,logUpdateFrame:!1}}},e={};function i(s){var n=e[s];if(void 0!==n)return n.exports;var o=e[s]={exports:{}};return t[s].call(o.exports,o,o.exports,i),o.exports}var s={};return(()=>{var t,e=s;const n=i(449),o=i(703),r=i(832),l=i(625),a=i(960);class h{constructor(e={},i){var s;if(this[t]=[],this.options=Object.assign(Object.assign({},a.DefaultOptions),e),!this.options.uri)throw new Error("URI is required");const o=`ws${this.options.secure?"s":""}://${this.options.uri.split("/")[0]}:${this.options.port}/${null!==(s=this.options.uri.split("/")[1])&&void 0!==s?s:""}`;this.ws=new WebSocket(o),this.ping=0,this.space="default",this.events=new Map,this.self={},this.tps=0,this.all={},this.teams=new l.MultyxClientObject(this,{},[],!0),this.clients={},this.controller=new r.Controller(this.ws),null==i||i(),this.ws.onmessage=t=>{var e,i,s,o;const r=n.Message.Parse(t.data);this.ping=2*(Date.now()-r.time),r.native?(this.parseNativeEvent(r),null===(e=this.events.get(h.Native))||void 0===e||e.forEach(t=>t(r))):(null===(i=this.events.get(r.name))||void 0===i||i.forEach(t=>{const e=t(r.data);void 0!==e&&this.send(r.name,e)}),null===(s=this.events.get(h.Custom))||void 0===s||s.forEach(t=>t(r))),null===(o=this.events.get(h.Any))||void 0===o||o.forEach(t=>t(r))}}on(t,e){var i;const s=null!==(i=this.events.get(t))&&void 0!==i?i:[];s.push(e),this.events.set(t,s)}send(t,e){"_"===t[0]&&(t="_"+t);const i={instruction:"resp",name:t,response:e};this.ws.send(n.Message.Native(i))}await(t,e){return this.send(t,e),new Promise(e=>this.events.set(Symbol.for("_"+t),[e]))}loop(t,e){if(e)this.on(h.Start,()=>setInterval(t,Math.round(1e3/e)));else{const e=()=>{t(),requestAnimationFrame(e)};this.on(h.Start,()=>requestAnimationFrame(e))}}[(t=o.Done,o.Add)](t){this[o.Done].push(t)}parseNativeEvent(t){var e,i,s,r,a;t.data=t.data.map(n.UncompressUpdate),this.options.logUpdateFrame&&console.log(t.data);for(const n of t.data)switch(n.instruction){case"init":this.initialize(n);for(const t of null!==(e=this.events.get(h.Start))&&void 0!==e?e:[])this[o.Done].push(()=>t(n));this.events.has(h.Start)&&(this.events.get(h.Start).length=0);break;case"edit":if(1==n.path.length)n.team?this.teams.set(n.path[0],new o.EditWrapper(n.value)):this.clients[n.path[0]]=new l.MultyxClientObject(this,new o.EditWrapper(n.value),[n.path[0]],!1);else{const t=n.team?this.teams.get(n.path[0]):this.clients[n.path[0]];if(!t)return;t.set(n.path.slice(1),new o.EditWrapper(n.value))}for(const t of null!==(i=this.events.get(h.Edit))&&void 0!==i?i:[])this[o.Done].push(()=>t(n));break;case"self":this.parseSelf(n);break;case"conn":this.clients[n.uuid]=new l.MultyxClientObject(this,n.data,[n.uuid],!1);for(const t of null!==(s=this.events.get(h.Connection))&&void 0!==s?s:[])this[o.Done].push(()=>t(this.clients[n.uuid]));break;case"dcon":for(const t of null!==(r=this.events.get(h.Disconnect))&&void 0!==r?r:[]){const e=this.clients[n.client].value;this[o.Done].push(()=>t(e))}delete this.clients[n.client];break;case"resp":{const t=null===(a=this.events.get(Symbol.for("_"+n.name)))||void 0===a?void 0:a[0];this.events.delete(Symbol.for("_"+n.name)),t&&this[o.Done].push(()=>t(n.response));break}default:this.options.verbose&&console.error("Server error: Unknown native Multyx instruction")}this[o.Done].forEach(t=>t()),this[o.Done].length=0}initialize(t){this.tps=t.tps,this.uuid=t.client.uuid,this.joinTime=t.client.joinTime,this.controller.listening=new Set(t.client.controller);for(const e of Object.keys(t.teams))this.teams[e]=new o.EditWrapper(t.teams[e]);this.all=this.teams.all,this.clients={};for(const[e,i]of Object.entries(t.clients))e!=this.uuid&&(this.clients[e]=new l.MultyxClientObject(this,new o.EditWrapper(i),[e],!1));const e=new l.MultyxClientObject(this,new o.EditWrapper(t.client.self),[this.uuid],!0);this.self=e,this.clients[this.uuid]=e;for(const[e,i]of Object.entries(t.constraintTable))(this.uuid==e?this.self:this.teams[e])[o.Unpack](i)}parseSelf(t){if("controller"==t.property)this.controller.listening=new Set(t.data);else if("uuid"==t.property)this.uuid=t.data;else if("constraint"==t.property){let e=this.uuid==t.data.path[0]?this.self:this.teams[t.data.path[0]];for(const i of t.data.path.slice(1))e=null==e?void 0:e[i];if(void 0===e)return;e[o.Unpack]({[t.data.name]:t.data.args})}else"space"==t.property&&(this.space=t.data,this.updateSpace())}updateSpace(){"default"!=this.space?document.querySelectorAll("[data-multyx-space]").forEach(t=>{t.style.display=t.dataset.multyxSpace==this.space?"block":"none",t.style.pointerEvents=t.dataset.multyxSpace==this.space?"auto":"none"}):document.querySelectorAll("[data-multyx-space]").forEach(t=>{t.style.display="block",t.style.pointerEvents="auto"})}}h.Start=Symbol("start"),h.Connection=Symbol("connection"),h.Disconnect=Symbol("disconnect"),h.Edit=Symbol("edit"),h.Native=Symbol("native"),h.Custom=Symbol("custom"),h.Any=Symbol("any"),h.Interpolate=o.Interpolate,e.default=h})(),s.default})());
1
+ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Multyx=e():t.Multyx=e()}(self,()=>(()=>{"use strict";var t={249:function(t,e,i){var s,n=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const o=i(625),r=i(703),l=n(i(416)),a=i(449);class h{addEditCallback(t){this.editCallbacks.push(t)}get value(){var t;const e=[];for(let i=0;i<this.length;i++)e[i]=null===(t=this.get(i))||void 0===t?void 0:t.value;return e}get length(){return this.list.length}set length(t){this.list.length=t}handleShiftOperation(t,e){const i=t>=0?e>=0?"right":"left":0==e?"reverse":e<0?"length":"unknown";switch(i){case"reverse":for(let t=0;t<Math.floor(this.length/2);t++){const e=this.list[t];this.list[t]=this.list[this.length-1-t],this.list[this.length-1-t]=e}break;case"left":for(let i=t;i<this.length;i++)i+e<0||(this.list[i+e]=this.list[i]);break;case"right":for(let i=this.length-1;i>=t;i--)this.list[i+e]=this.list[i];break;case"length":this.length+=e;break;default:this.multyx.options.verbose&&console.error("Unknown shift operation: "+i)}}constructor(t,e,i=[],n){this.type="list",this.editCallbacks=[],this.toString=()=>this.value.toString(),this.valueOf=()=>this.value,this[s]=()=>this.value,this.list=[],this.propertyPath=i,this.multyx=t,this.editable=n;const o=e instanceof r.EditWrapper;e instanceof h&&(e=e.value),e instanceof r.EditWrapper&&(e=e.value);for(let t=0;t<e.length;t++)this.set(t,o?new r.EditWrapper(e[t]):e[t]);return new Proxy(this,{has:(t,e)=>"number"==typeof e?t.has(e):e in t,get:(t,e)=>e in t?t[e]:(isNaN(parseInt(e))||(e=parseInt(e)),t.get(e)),set:(t,e,i)=>e in t?(t[e]=i,!0):!!t.set(e,i),deleteProperty:(t,e)=>"number"==typeof e&&t.delete(e)})}has(t){return t>=0&&t<this.length}get(t){if("number"==typeof t)return this.list[t];if(0==t.length)return this;if(1==t.length)return this.list[parseInt(t[0])];const e=this.list[parseInt(t[0])];return!e||e instanceof o.MultyxClientValue?void 0:e.get(t.slice(1))}recursiveSet(t,e){if(0==t.length)return this.multyx.options.verbose&&console.error(`Attempting to edit MultyxClientList with no path. Setting '${this.propertyPath.join(".")}' to ${e}`),!1;if("shift"==t[0]&&e instanceof r.EditWrapper)return this.handleShiftOperation(parseInt(t[1]),e.value),!0;if(1==t.length)return this.set(parseInt(t[0]),e);let i=this.get(parseInt(t[0]));return(i instanceof o.MultyxClientValue||null==i)&&(this.set(parseInt(t[0]),new r.EditWrapper({})),i=this.get(parseInt(t[0]))),!(!i||i instanceof o.MultyxClientValue)&&i.set(t.slice(1),e)}set(t,e){if(Array.isArray(t))return this.recursiveSet(t,e);const i=this.get(t),s=e instanceof r.EditWrapper,n=s||this.editable,a=s||(0,o.IsMultyxClientItem)(e)?e.value:e;if(void 0===a)return this.delete(t,s);if(s&&this.tryApplyServerValue(t,a,i))return!0;if(this.list[t]instanceof o.MultyxClientValue&&("object"!=typeof a||null===a)){const e=this.list[t].set(s?new r.EditWrapper(a):a);return this.enqueueEditCallbacks(t,i),e}return n?(this.list[t]=new((0,l.default)(a))(this.multyx,s?new r.EditWrapper(a):a,[...this.propertyPath,t.toString()],this.editable),this.notifyIndexWaiters(t),this.enqueueEditCallbacks(t,i),!0):(this.multyx.options.verbose&&console.error(`Attempting to set property that is not editable. Setting '${this.propertyPath.join(".")+"."+t}' to ${a}`),!1)}delete(t,e=!1){const i=this.get(t);if("string"==typeof t&&(t=parseInt(t)),!this.editable&&!e)return this.multyx.options.verbose&&console.error(`Attempting to delete property that is not editable. Deleting '${this.propertyPath.join(".")+"."+t}'`),!1;delete this.list[t];for(const e of this.editCallbacks)this.multyx[r.Add](()=>e(t,void 0,i));return e||this.multyx.ws.send(a.Message.Native({instruction:"edit",path:[...this.propertyPath,t.toString()],value:void 0})),!0}await(t){if(this.has(t))return Promise.resolve(this.get(t));const e=Symbol.for("_"+this.propertyPath.join(".")+"."+t);return new Promise(t=>this.multyx.on(e,t))}push(...t){for(const e of t)this.set(this.length,e);return this.length}pop(){if(0===this.length)return;const t=this.get(this.length);return this.delete(this.length),t}unshift(...t){for(let e=this.length-1;e>=0;e--)e>=t.length?this.set(e,this.get(e-t.length)):this.set(e,t[e]);return this.length}shift(){if(0==this.length)return;this.length--;const t=this.get(0);for(let t=0;t<this.length;t++)this.set(t,this.get(t+1));return t}slice(t,e){return this.list.slice(t,e)}splice(t,e,...i){return this.list.splice(t,null!=e?e:0,...i)}setSplice(t,e,...i){void 0===e&&(e=this.length-t);let s=i.length-e;if(s>0)for(let i=this.length-1;i>=t+e;i--)this.set(i+s,this.get(i));else if(s<0){for(let i=t+e;i<this.length;i++)this.set(i+s,this.get(i));const i=this.length;for(let t=i+s;t<i;t++)this.set(t,void 0)}for(let e=t;e<i.length;e++)this.set(e,i[e])}filter(t){return this.list.filter((e,i)=>t(e,i,this))}setFilter(t){const e=[];for(let i=0;i<this.length;i++)e.push(t(this.get(i),i,this));let i=0;for(let t=0;t<e.length;t++)e[t]&&i&&this.set(t-i,this.get(t)),e[t]||i--}map(t){const e=[];for(let i=0;i<this.length;i++)e.push(t(this.get(i),i,this));return e}flat(){return this.list.flat()}setFlat(){for(let t=0;t<this.length;t++){const e=this.get(t);if(e instanceof h)for(let i=0;i<e.length;i++)t++,this.set(t,e[i])}}reduce(t,e){for(let i=0;i<this.length;i++)e=t(e,this.get(i),i,this);return e}reduceRight(t,e){for(let i=this.length-1;i>=0;i--)e=t(e,this.get(i),i,this);return e}reverse(){let t=this.length-1;for(let e=0;e<t;e++){const i=this.get(e),s=this.get(t);this.set(e,s),this.set(t,i)}return this}forEach(t){for(let e=0;e<this.length;e++)t(this.get(e),e,this)}every(t){for(let e=0;e<this.length;e++)if(!t(this.get(e),e,this))return!1;return!0}some(t){for(let e=0;e<this.length;e++)if(t(this.get(e),e,this))return!0;return!1}find(t){for(let e=0;e<this.length;e++)if(t(this.get(e),e,this))return this.get(e)}findIndex(t){for(let e=0;e<this.length;e++)if(t(this.get(e),e,this))return e;return-1}entries(){const t=[];for(let e=0;e<this.length;e++)t.push([this.get(e),e]);return t}keys(){return Array(this.length).fill(0).map((t,e)=>e)}[r.Edit](){}[r.Unpack](t){var e;for(let i=0;i<this.length;i++)null===(e=this.get(i))||void 0===e||e[r.Unpack](t[i])}[Symbol.iterator](){const t=[];for(let e=0;e<this.length;e++)t[e]=this.get(e);return t[Symbol.iterator]()}hydrateFromServer(t){if(Array.isArray(t)){for(let e=0;e<t.length;e++)this.set(e,new r.EditWrapper(t[e]));for(let e=t.length;e<this.length;e++)this.delete(e,!0);this.length=t.length}}tryApplyServerValue(t,e,i){const s=this.list[t];if(!s)return!1;if(s instanceof o.MultyxClientValue&&("object"!=typeof e||null===e))return s.set(new r.EditWrapper(e)),this.enqueueEditCallbacks(t,i),!0;const n="function"==typeof(null==s?void 0:s.hydrateFromServer);return Array.isArray(e)&&n&&"list"===s.type?(s.hydrateFromServer(e),this.enqueueEditCallbacks(t,i),!0):!(null===(l=e)||"object"!=typeof l||Array.isArray(l)||!n||"object"!==s.type||(s.hydrateFromServer(e),this.enqueueEditCallbacks(t,i),0));var l}notifyIndexWaiters(t){var e,i;const s=Symbol.for("_"+this.propertyPath.join(".")+"."+t);this.multyx.events.has(s)&&this.multyx[r.Done].push(...null!==(i=null===(e=this.multyx.events.get(s))||void 0===e?void 0:e.map(e=>()=>e(this.list[t])))&&void 0!==i?i:[])}enqueueEditCallbacks(t,e){for(const i of this.editCallbacks)this.multyx[r.Add](()=>i(t,this.get(t),e))}}s=Symbol.toPrimitive,e.default=h},280:(t,e,i)=>{var s;Object.defineProperty(e,"__esModule",{value:!0});const n=i(449),o=i(703);class r{get value(){return this.readModifiers.reduce((t,e)=>e(t),this._value)}set value(t){this._value=t,this.captureSample(t)}addReadModifier(t){this.readModifiers.push(t)}addEditCallback(t){this.editCallbacks.push(t)}[o.Edit](t,e){0==t.length&&this.set(new o.EditWrapper(e))}constructor(t,e,i=[],n){var r,l;this.readModifiers=[],this.editCallbacks=[],this.interpolationFrameMs=250,this.toString=()=>this.value.toString(),this.valueOf=()=>this.value,this[s]=()=>this.value,this.propertyPath=i,this.editable=n,this.multyx=t,this.constraints={},this.set(e);const a=Symbol.for("_"+this.propertyPath.join("."));this.multyx.events.has(a)&&this.multyx[o.Done].push(...null!==(l=null===(r=this.multyx.events.get(a))||void 0===r?void 0:r.map(t=>()=>t(this.value)))&&void 0!==l?l:[])}set(t){if(t instanceof o.EditWrapper){const e=this.value;return this.value=t.value,this.editCallbacks.forEach(i=>i(t.value,e)),!0}if(!this.editable)return this.multyx.options.verbose&&console.error(`Attempting to set property that is not editable. Setting '${this.propertyPath.join(".")}' to ${t}`),!1;let e=t;for(const i in this.constraints)if(e=(0,this.constraints[i])(e),null===e)return this.multyx.options.verbose&&console.error(`Attempting to set property that failed on constraint. Setting '${this.propertyPath.join(".")}' to ${t}, stopped by constraint '${i}'`),!1;return this._value===e?(this.value=e,!0):(this.value=e,this.multyx.ws.send(n.Message.Native({instruction:"edit",path:this.propertyPath,value:e})),!0)}bindElement(t){this.addEditCallback((e,i)=>{e!==i&&(t.innerText=e.toString())})}[o.Unpack](t){for(const[e,i]of Object.entries(t)){const t=(0,o.BuildConstraint)(e,i);t&&(this.constraints[e]=t)}}Lerp(t=250){return this.applyInterpolation("lerp",t)}PredictiveLerp(t=250){return this.applyInterpolation("predictive",t)}applyInterpolation(t,e){if("number"!=typeof this._value||Number.isNaN(this._value))throw new Error(`MultyxClientValue.${"lerp"===t?"Lerp":"PredictiveLerp"} can only be applied to numeric values`);return this.interpolationFrameMs=Math.max(1,e),this.attachInterpolationModifier(t),this}attachInterpolationModifier(t){this.interpolationModifier&&(this.readModifiers=this.readModifiers.filter(t=>t!==this.interpolationModifier)),this.interpolationModifier=e=>this.interpolateValue(e,t),this.readModifiers.push(this.interpolationModifier)}captureSample(t){if("number"!=typeof t||Number.isNaN(t))return this.latestSample=void 0,void(this.previousSample=void 0);const e=Date.now();this.latestSample?(this.previousSample=Object.assign({},this.latestSample),this.latestSample={value:t,time:e}):this.latestSample={value:t,time:e}}interpolateValue(t,e){if("number"!=typeof t||!this.latestSample||!this.previousSample)return t;const i=this.latestSample.time-this.previousSample.time;if(i<=0)return t;const s=Math.max(1,Math.min(i,this.interpolationFrameMs)),n=Math.max(0,Math.min(Date.now()-this.latestSample.time,s)),o=0===s?1:n/s,r=this.latestSample.value-this.previousSample.value;return"predictive"===e?this.latestSample.value+r*o:this.previousSample.value+r*o}}s=Symbol.toPrimitive,e.default=r},416:(t,e,i)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(t){return Array.isArray(t)?i(249).default:"object"==typeof t?i(922).default:i(280).default}},449:(t,e)=>{function i(t){let e,i;if("edit"==t.instruction?(e=0,i=[t.path.join("."),JSON.stringify(t.value)]):"input"==t.instruction?(e=1,i=[t.input,JSON.stringify(t.data)]):"resp"==t.instruction&&(e=2,i=[t.name,JSON.stringify(t.response)]),!i||void 0===e)return"";let s=e.toString();for(let t=0;t<i.length;t++)s+=i[t].replace(/;/g,";_"),t<i.length-1&&(s+=";,");return JSON.stringify([s])}Object.defineProperty(e,"__esModule",{value:!0}),e.Message=void 0,e.UncompressUpdate=function(t){const[e,...i]=t.split(/;,/),s=e[0],n=e.slice(1).replace(/;_/g,";"),o=i.map(t=>t.replace(/;_/g,";")).map(t=>"undefined"==t?void 0:JSON.parse(t));return"0"==s?{instruction:"edit",team:!1,path:n.split("."),value:o[0]}:"1"==s?{instruction:"edit",team:!0,path:n.split("."),value:o[0]}:"2"==s?{instruction:"self",property:"controller",data:JSON.parse(n)}:"3"==s?{instruction:"self",property:"uuid",data:JSON.parse(n)}:"4"==s?{instruction:"self",property:"constraint",data:JSON.parse(n)}:"9"==s?{instruction:"self",property:"space",data:JSON.parse(n)}:"5"==s?{instruction:"resp",name:n,response:o[0]}:"6"==s?{instruction:"conn",uuid:n,data:o[0]}:"7"==s?{instruction:"dcon",client:n}:"8"==s?{instruction:"init",client:JSON.parse(n),tps:o[0],constraintTable:o[1],clients:o[2],teams:o[3],space:o[4]}:void 0},e.CompressUpdate=i;class s{constructor(t,e,i=!1){this.name=t,this.data=e,this.time=Date.now(),this.native=i}static BundleOperations(t,e){return Array.isArray(e)||(e=[e]),JSON.stringify(new s("_",{operations:e,deltaTime:t}))}static Native(t){return i(t)}static Parse(t){var e,i;const n=JSON.parse(t);return Array.isArray(n)?new s("_",n,!0):new s(null!==(e=n.name)&&void 0!==e?e:"",null!==(i=n.data)&&void 0!==i?i:"",!1)}static Create(t,e){if(0==t.length)throw new Error("Multyx message cannot have empty name");if("_"==t[0]&&(t="_"+t),"function"==typeof e)throw new Error("Multyx data must be JSON storable");return JSON.stringify(new s(t,e))}}e.Message=s},625:function(t,e,i){var s=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0}),e.MultyxClientValue=e.MultyxClientObject=e.MultyxClientList=void 0,e.IsMultyxClientItem=function(t){return t instanceof n.default||t instanceof o.default||t instanceof r.default};const n=s(i(249));e.MultyxClientList=n.default;const o=s(i(922));e.MultyxClientObject=o.default;const r=s(i(280));e.MultyxClientValue=r.default},703:(t,e)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.EditWrapper=e.Edit=e.Add=e.Done=e.Unpack=void 0,e.Interpolate=function(t,e,i){if(!Array.isArray(i)||0===i.length)throw new Error("Interpolation curve must contain at least one slice");const s=[...i].sort((t,e)=>t.time-e.time),n=s[s.length-1].time,o=n<=1;let r={value:t[e],time:Date.now()},l={value:t[e],time:Date.now()};Object.defineProperty(t,e,{configurable:!0,enumerable:!0,get:()=>{if(r.time===l.time)return l.value;const t=Date.now(),e=Math.max(l.time-r.time,1),i=Math.max(0,t-l.time),a=(h=o?i/e:i,u=0,p=n,Math.min(Math.max(h,u),p));var h,u,p;let c,d=s[0],f=s[s.length-1];for(const t of s){if(!(t.time<=a)){f=t;break}d=t}if(f.time===d.time)c=d.progress;else{const t=(a-d.time)/(f.time-d.time);c=d.progress+t*(f.progress-d.progress)}return Number.isNaN(c)?r.value:"number"==typeof r.value&&"number"==typeof l.value?l.value*c+r.value*(1-c):c>=1?l.value:r.value},set:t=>{const e=Date.now();return e-l.time<10?(l.value=t,l.time=e,!0):(r=Object.assign({},l),l={value:t,time:e},!0)}})},e.BuildConstraint=function(t,e){return"min"==t?t=>t>=e[0]?t:e[0]:"max"==t?t=>t<=e[0]?t:e[0]:"int"==t?t=>Math.floor(t):"ban"==t?t=>e.includes(t)?null:t:"disabled"==t?t=>e[0]?null:t:t=>t},e.Unpack=Symbol("unpack"),e.Done=Symbol("done"),e.Add=Symbol("add"),e.Edit=Symbol("edit"),e.EditWrapper=class{constructor(t){this.value=t}}},832:(t,e,i)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.Controller=void 0;const s=i(449);e.Controller=class{constructor(t){this.listening=new Set,this.ws=t,this.preventDefault=!1,this.keys={},this.mouse={x:NaN,y:NaN,down:!1,centerX:0,centerY:0,scaleX:1,scaleY:1},document.addEventListener("keydown",t=>{this.preventDefault&&t.preventDefault();const e=t.key.toLowerCase();this.keys[e]&&this.listening.has("keyhold")&&this.relayInput("keyhold",{code:e}),this.keys[t.code]&&this.listening.has("keyhold")&&this.relayInput("keyhold",{code:t.code}),this.listening.has(e)&&!this.keys[e]&&this.relayInput("keydown",{code:t.key}),this.listening.has(t.code)&&!this.keys[t.code]&&this.relayInput("keydown",{code:t.code}),this.keys[e]=!0,this.keys[t.code]=!0}),document.addEventListener("keyup",t=>{this.preventDefault&&t.preventDefault();const e=t.key.toLowerCase();delete this.keys[e],delete this.keys[t.code],this.listening.has(e)&&this.relayInput("keyup",{code:e}),this.listening.has(t.code)&&this.relayInput("keyup",{code:t.code})}),document.addEventListener("mousedown",t=>{if(this.preventDefault&&t.preventDefault(),this.mouseGetter){const t=this.mouseGetter();this.mouse.x=t.x,this.mouse.y=t.y}else this.mouse.x=(t.clientX-this.mouse.centerX)/this.mouse.scaleX,this.mouse.y=(t.clientY-this.mouse.centerY)/this.mouse.scaleY;this.mouse.down=!0,this.listening.has("mousedown")&&this.relayInput("mousedown",{x:this.mouse.x,y:this.mouse.y})}),document.addEventListener("mouseup",t=>{if(this.preventDefault&&t.preventDefault(),this.mouseGetter){const t=this.mouseGetter();this.mouse.x=t.x,this.mouse.y=t.y}else this.mouse.x=(t.clientX-this.mouse.centerX)/this.mouse.scaleX,this.mouse.y=(t.clientY-this.mouse.centerY)/this.mouse.scaleY;this.mouse.down=!1,this.listening.has("mouseup")&&this.relayInput("mouseup",{x:this.mouse.x,y:this.mouse.y})}),document.addEventListener("mousemove",t=>{if(this.preventDefault&&t.preventDefault(),this.mouseGetter){const t=this.mouseGetter();this.mouse.x=t.x,this.mouse.y=t.y}else this.mouse.x=(t.clientX-this.mouse.centerX)/this.mouse.scaleX,this.mouse.y=(t.clientY-this.mouse.centerY)/this.mouse.scaleY;this.listening.has("mousemove")&&this.relayInput("mousemove",{x:this.mouse.x,y:this.mouse.y})})}mapCanvasPosition(t,e){const i="top"in e,s="bottom"in e,n="left"in e,o="right"in e,r=e.anchor,l=t.getBoundingClientRect(),a=(t,...e)=>{const i=t?"Cannot include value for ":"Must include value for ",s=1==e.length?e[0]:e.slice(0,-1).join(", ")+(t?" and ":" or ")+e.slice(-1)[0],n=r?" if anchoring at "+r:" if not anchoring";console.error(i+s+n)},h=l.width/l.height,u=l.height/l.width;if((Number.isNaN(h)||Number.isNaN(u))&&console.error("Canvas element bounding box is flat, canvas must be present on the screen"),r){if("center"==r){if(i&&s&&e.top!==-e.bottom||n&&o&&e.left!==-e.right)return a(!0,"top","bottom","left","right");i?(e.left=n?e.left:o?-e.right:-Math.abs(h*e.top),e.right=n?-e.left:o?e.right:Math.abs(h*e.top),e.bottom=-e.top):s?(e.left=n?e.left:o?-e.right:-Math.abs(h*e.bottom),e.right=n?-e.left:o?e.right:Math.abs(h*e.bottom),e.top=-e.bottom):n?(e.top=i?e.top:s?-e.bottom:-Math.abs(u*e.left),e.bottom=i?-e.top:s?e.bottom:Math.abs(u*e.left),e.right=-e.left):o&&(e.top=i?e.top:s?-e.bottom:-Math.abs(u*e.right),e.bottom=i?-e.top:s?e.bottom:Math.abs(u*e.right),e.left=-e.right)}else if("bottom"==r){if(!n&&!o&&!i)return a(!1,"left","right","top");if(e.bottom)return a(!0,"bottom");e.bottom=0,n?(e.top=Math.abs(u*e.left*2),e.right=-e.left):o?(e.top=Math.abs(u*e.right*2),e.left=-e.right):(e.left=-Math.abs(h*e.top/2),e.right=-e.left)}else if("top"==r){if(!n&&!o&&!s)return a(!1,"left","right","bottom");if(e.top)return a(!0,"top");e.top=0,n?(e.bottom=Math.abs(u*e.left*2),e.right=-e.left):o?(e.bottom=Math.abs(u*e.right*2),e.left=-e.right):(e.left=-Math.abs(h*e.bottom/2),e.right=-e.left)}else if("left"==r){if(!i&&!s&&!o)return a(!1,"top","bottom","right");if(n)return a(!0,"left");e.left=0,i?(e.right=-Math.abs(h*e.top*2),e.bottom=-e.top):s?(e.right=Math.abs(h*e.bottom*2),e.top=-e.bottom):(e.top=-Math.abs(u*e.right/2),e.bottom=-e.top)}else if("right"==r){if(!i&&!s&&!n)return a(!1,"top","bottom","left");if(o)return a(!0,"right");e.right=0,i?(e.left=-Math.abs(h*e.top*2),e.bottom=-e.top):s?(e.left=Math.abs(h*e.bottom*2),e.top=-e.bottom):(e.top=-Math.abs(u*e.right/2),e.bottom=-e.top)}else if("topleft"==r){if(!o&&!s)return a(!1,"right","bottom");if(n||i)return a(!0,"left","top");e.left=e.top=0,o?e.bottom=Math.abs(u*e.right):e.right=Math.abs(h*e.bottom)}else if("topright"==r){if(!n&&!s)return a(!1,"left","bottom");if(o||i)return a(!0,"right","top");e.right=e.top=0,n?e.bottom=Math.abs(u*e.left):e.left=Math.abs(h*e.bottom)}else if("bottomleft"==r){if(!o&&!i)return a(!1,"right","top");if(s||n)return a(!0,"bottom","left");e.left=e.bottom=0,o?e.top=Math.abs(u*e.right):e.right=Math.abs(h*e.top)}else if("bottomright"==r){if(!i&&!n)return a(!1,"top","left");if(o||s)return a(!0,"bottom","right");e.right=e.bottom=0,n?e.top=Math.abs(u*e.left):e.left=Math.abs(h*e.top)}}else{if(!i&&!s)return a(!1,"top","bottom");if(s?i||(e.top=e.bottom-t.height):e.bottom=e.top+t.height,!n&&!o)return a(!1,"left","right");o?n||(e.left=e.right-t.width):e.right=e.left+t.width}const p=t.getContext("2d");null==p||p.setTransform(1,0,0,1,0,0),t.width=Math.floor(Math.abs(e.right-e.left)),t.height=Math.floor(Math.abs(e.bottom-e.top)),e.right<e.left&&(null==p||p.scale(-1,1)),e.top>e.bottom&&(null==p||p.scale(1,-1)),null==p||p.translate(-e.left,-e.top)}mapMousePosition(t,e,i=document.body,s=1,n=s){const o=window.innerWidth/(i instanceof HTMLCanvasElement?i.width:i.clientWidth),r=window.innerHeight/(i instanceof HTMLCanvasElement?i.height:i.clientHeight),l=i.getBoundingClientRect();this.mouse.centerX=l.left+t*o,this.mouse.centerY=l.top+e*r,this.mouse.scaleX=s*o,this.mouse.scaleY=n*r}mapMouseToCanvas(t){const e=t.getContext("2d"),i=null==e?void 0:e.getTransform(),s=t.getBoundingClientRect(),n=s.width/t.width,o=s.height/t.height;this.mouse.centerX=s.left+(null==i?void 0:i.e)*n,this.mouse.centerY=s.top+(null==i?void 0:i.f)*o,this.mouse.scaleX=n*(null==i?void 0:i.a),this.mouse.scaleY=o*(null==i?void 0:i.d)}setMouseAs(t){this.mouseGetter=t}relayInput(t,e){if(1!==this.ws.readyState)throw new Error("Websocket connection is "+(2==this.ws.readyState?"closing":"closed"));this.ws.send(s.Message.Native(Object.assign({instruction:"input",input:t},e?{data:e}:{})))}}},922:function(t,e,i){var s=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const n=i(449),o=i(703),r=i(625),l=s(i(416)),a=s(i(280)),h=t=>null!==t&&"object"==typeof t&&!Array.isArray(t);class u{get value(){const t={};for(const e in this.object)t[e]=this.object[e];return t}onWrite(t){this.writeCallbacks.push(t)}[o.Edit](t,e){var i;1!=t.length?(0==t.length&&this.multyx.options.verbose&&console.error("Update path is empty. Attempting to edit MultyxClientObject with no path."),this.has(t[0])||this.set(t[0],new o.EditWrapper({})),null===(i=this.get(t[0]))||void 0===i||i[o.Edit](t.slice(1),e)):this.set(t[0],new o.EditWrapper(e))}constructor(t,e,i=[],s){this.type="object",this.writeCallbacks=[],this.object={},this.propertyPath=i,this.multyx=t,this.editable=s;const n=e instanceof o.EditWrapper;e instanceof u&&(e=e.value),e instanceof o.EditWrapper&&(e=e.value);for(const t in e)this.set(t,n?new o.EditWrapper(e[t]):e[t]);if(this.constructor===u)return new Proxy(this,{has:(t,e)=>t.has(e),get:(t,e)=>e in t?t[e]:t.get(e),set:(t,e,i)=>e in t?(t[e]=i,!0):t.set(e,i),deleteProperty:(t,e)=>t.delete(e,!1)})}has(t){return t in this.object}get(t){if("string"==typeof t)return this.object[t];if(0==t.length)return this;if(1==t.length)return this.object[t[0]];const e=this.object[t[0]];return!e||e instanceof a.default?void 0:e.get(t.slice(1))}recursiveSet(t,e){if(0==t.length)return this.multyx.options.verbose&&console.error(`Attempting to edit MultyxClientObject with no path. Setting '${this.propertyPath.join(".")}' to ${e}`),!1;if(1==t.length)return this.set(t[0],e);let i=this.get(t[0]);return(i instanceof a.default||null==i)&&(isNaN(parseInt(t[1]))?(this.set(t[0],new o.EditWrapper({})),i=this.get(t[0])):(this.set(t[0],new o.EditWrapper([])),i=this.get(t[0]))),i.set(t.slice(1),e)}set(t,e){if(Array.isArray(t))return this.recursiveSet(t,e);const i=this.get(t),s=e instanceof o.EditWrapper,n=s||this.editable,h=s||(0,r.IsMultyxClientItem)(e)?e.value:e;if(void 0===h)return this.delete(t,s);if(s&&this.tryApplyServerValue(t,h,i))return!0;if(this.object[t]instanceof a.default&&("object"!=typeof h||null===h)){const e=this.object[t].set(s?new o.EditWrapper(h):h);return this.enqueueWriteCallbacks(t,i),e}return n?(this.object[t]=new((0,l.default)(h))(this.multyx,s?new o.EditWrapper(h):h,[...this.propertyPath,t],this.editable),this.notifyPropertyWaiters(t),this.enqueueWriteCallbacks(t,i),!0):(this.multyx.options.verbose&&console.error(`Attempting to set property that is not editable. Setting '${this.propertyPath.join(".")+"."+t}' to ${h}`),!1)}delete(t,e=!1){const i="string"==typeof t?t:String(t),s=this.get(i);if(!this.editable&&!e)return this.multyx.options.verbose&&console.error(`Attempting to delete property that is not editable. Deleting '${this.propertyPath.join(".")+"."+t}'`),!1;delete this.object[t];for(const t of this.writeCallbacks)this.multyx[o.Add](()=>t(i,void 0,s));return e||this.multyx.ws.send(n.Message.Native({instruction:"edit",path:[...this.propertyPath,t],value:void 0})),!0}keys(){return Object.keys(this.object)}values(){return Object.values(this.object)}entries(){const t=[];for(let e in this.object)t.push([e,this.get(e)]);return t}await(t){if(this.has(t))return Promise.resolve(this.get(t));const e=Symbol.for("_"+this.propertyPath.join(".")+"."+t);return new Promise(t=>this.multyx.on(e,t))}[o.Unpack](t){var e;for(const i in t)null===(e=this.object[i])||void 0===e||e[o.Unpack](t[i])}hydrateFromServer(t){if(!h(t))return;const e=new Set(Object.keys(this.object));for(const[i,s]of Object.entries(t))e.delete(i),this.set(i,new o.EditWrapper(s));for(const t of e)this.delete(t,!0)}tryApplyServerValue(t,e,i){const s=this.object[t];if(!s)return!1;if(s instanceof a.default&&("object"!=typeof e||null===e))return s.set(new o.EditWrapper(e)),this.enqueueWriteCallbacks(t,i),!0;const n="function"==typeof(null==s?void 0:s.hydrateFromServer);return(Array.isArray(e)&&n&&"list"===s.type||!(!h(e)||!n||"object"!==s.type))&&(s.hydrateFromServer(e),this.enqueueWriteCallbacks(t,i),!0)}notifyPropertyWaiters(t){var e,i;const s=Symbol.for("_"+this.propertyPath.join(".")+"."+t);this.multyx.events.has(s)&&this.multyx[o.Done].push(...null!==(i=null===(e=this.multyx.events.get(s))||void 0===e?void 0:e.map(e=>()=>e(this.object[t])))&&void 0!==i?i:[])}enqueueWriteCallbacks(t,e){for(const i of this.writeCallbacks)this.multyx[o.Add](()=>i(t,this.get(t),e))}}e.default=u},960:(t,e)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DefaultOptions=void 0,e.DefaultOptions={port:8443,secure:!1,uri:"localhost",verbose:!1,logUpdateFrame:!1}}},e={};function i(s){var n=e[s];if(void 0!==n)return n.exports;var o=e[s]={exports:{}};return t[s].call(o.exports,o,o.exports,i),o.exports}var s={};return(()=>{var t,e=s;const n=i(449),o=i(703),r=i(832),l=i(625),a=i(960);class h{constructor(e={},i){var s;if(this[t]=[],this.options=Object.assign(Object.assign({},a.DefaultOptions),e),!this.options.uri)throw new Error("URI is required");const o=`ws${this.options.secure?"s":""}://${this.options.uri.split("/")[0]}:${this.options.port}/${null!==(s=this.options.uri.split("/")[1])&&void 0!==s?s:""}`;this.ws=new WebSocket(o),this.ping=0,this.space="default",this.events=new Map,this.self={},this.tps=0,this.all={},this.teams=new l.MultyxClientObject(this,{},[],!0),this.clients={},this.controller=new r.Controller(this.ws),null==i||i(),this.ws.onmessage=t=>{var e,i,s,o;const r=n.Message.Parse(t.data);this.ping=2*(Date.now()-r.time),r.native?(this.parseNativeEvent(r),null===(e=this.events.get(h.Native))||void 0===e||e.forEach(t=>t(r))):(null===(i=this.events.get(r.name))||void 0===i||i.forEach(t=>{const e=t(r.data);void 0!==e&&this.send(r.name,e)}),null===(s=this.events.get(h.Custom))||void 0===s||s.forEach(t=>t(r))),null===(o=this.events.get(h.Any))||void 0===o||o.forEach(t=>t(r))}}on(t,e){var i;const s=null!==(i=this.events.get(t))&&void 0!==i?i:[];s.push(e),this.events.set(t,s)}send(t,e){"_"===t[0]&&(t="_"+t);const i={instruction:"resp",name:t,response:e};this.ws.send(n.Message.Native(i))}await(t,e){return this.send(t,e),new Promise(e=>this.events.set(Symbol.for("_"+t),[e]))}loop(t,e){if(e)this.on(h.Start,()=>setInterval(t,Math.round(1e3/e)));else{const e=()=>{t(),requestAnimationFrame(e)};this.on(h.Start,()=>requestAnimationFrame(e))}}[(t=o.Done,o.Add)](t){this[o.Done].push(t)}parseNativeEvent(t){var e,i,s,r,a;t.data=t.data.map(n.UncompressUpdate),this.options.logUpdateFrame&&console.log(t.data);for(const n of t.data)switch(n.instruction){case"init":this.initialize(n);for(const t of null!==(e=this.events.get(h.Start))&&void 0!==e?e:[])this[o.Done].push(()=>t(n));this.events.has(h.Start)&&(this.events.get(h.Start).length=0);break;case"edit":if(1==n.path.length)n.team?this.teams.set(n.path[0],new o.EditWrapper(n.value)):this.clients[n.path[0]]=new l.MultyxClientObject(this,new o.EditWrapper(n.value),[n.path[0]],!1);else{const t=n.team?this.teams.get(n.path[0]):this.clients[n.path[0]];if(!t)return;t.set(n.path.slice(1),new o.EditWrapper(n.value))}for(const t of null!==(i=this.events.get(h.Edit))&&void 0!==i?i:[])this[o.Done].push(()=>t(n));break;case"self":this.parseSelf(n);break;case"conn":this.clients[n.uuid]=new l.MultyxClientObject(this,n.data,[n.uuid],!1);for(const t of null!==(s=this.events.get(h.Connection))&&void 0!==s?s:[])this[o.Done].push(()=>t(this.clients[n.uuid]));break;case"dcon":for(const t of null!==(r=this.events.get(h.Disconnect))&&void 0!==r?r:[]){const e=this.clients[n.client].value;this[o.Done].push(()=>t(e))}delete this.clients[n.client];break;case"resp":{const t=null===(a=this.events.get(Symbol.for("_"+n.name)))||void 0===a?void 0:a[0];this.events.delete(Symbol.for("_"+n.name)),t&&this[o.Done].push(()=>t(n.response));break}default:this.options.verbose&&console.error("Server error: Unknown native Multyx instruction")}this[o.Done].forEach(t=>t()),this[o.Done].length=0}initialize(t){this.tps=t.tps,this.uuid=t.client.uuid,this.joinTime=t.client.joinTime,this.controller.listening=new Set(t.client.controller);for(const e of Object.keys(t.teams))this.teams[e]=new o.EditWrapper(t.teams[e]);this.all=this.teams.all,this.clients={};for(const[e,i]of Object.entries(t.clients))e!=this.uuid&&(this.clients[e]=new l.MultyxClientObject(this,new o.EditWrapper(i),[e],!1));const e=new l.MultyxClientObject(this,new o.EditWrapper(t.client.self),[this.uuid],!0);this.self=e,this.clients[this.uuid]=e;for(const[e,i]of Object.entries(t.constraintTable))(this.uuid==e?this.self:this.teams[e])[o.Unpack](i)}parseSelf(t){if("controller"==t.property)this.controller.listening=new Set(t.data);else if("uuid"==t.property)this.uuid=t.data;else if("constraint"==t.property){let e=this.uuid==t.data.path[0]?this.self:this.teams[t.data.path[0]];for(const i of t.data.path.slice(1))e=null==e?void 0:e[i];if(void 0===e)return;e[o.Unpack]({[t.data.name]:t.data.args})}else"space"==t.property&&(this.space=t.data,this.updateSpace())}updateSpace(){"default"!=this.space?document.querySelectorAll("[data-multyx-space]").forEach(t=>{t.style.display=t.dataset.multyxSpace==this.space?"block":"none",t.style.pointerEvents=t.dataset.multyxSpace==this.space?"auto":"none"}):document.querySelectorAll("[data-multyx-space]").forEach(t=>{t.style.display="block",t.style.pointerEvents="auto"})}}h.Start=Symbol("start"),h.Connection=Symbol("connection"),h.Disconnect=Symbol("disconnect"),h.Edit=Symbol("edit"),h.Native=Symbol("native"),h.Custom=Symbol("custom"),h.Any=Symbol("any"),h.Interpolate=o.Interpolate,e.default=h})(),s.default})());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multyx-client",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Framework designed to simplify the creation of multiplayer browser games by addressing the complexities of managing server-client communication, shared state, and input handling",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -1,13 +1,14 @@
1
1
  import { Message } from "../message";
2
2
  import { RawObject } from '../types';
3
- import { Done, Edit, EditWrapper, Unpack } from "../utils";
3
+ import { Add, Done, Edit, EditWrapper, Unpack } from "../utils";
4
4
 
5
5
  import type Multyx from '../index';
6
6
  import { IsMultyxClientItem, type MultyxClientList, type MultyxClientItem } from ".";
7
7
  import MultyxClientItemRouter from "./router";
8
8
  import MultyxClientValue from "./value";
9
9
 
10
- const isPlainObject = (value: any) => value !== null && typeof value === 'object' && !Array.isArray(value);
10
+ const isPlainObject = (value: unknown): value is Record<string, unknown> =>
11
+ value !== null && typeof value === 'object' && !Array.isArray(value);
11
12
 
12
13
  export default class MultyxClientObject {
13
14
  readonly type = 'object';
@@ -16,7 +17,7 @@ export default class MultyxClientObject {
16
17
  propertyPath: string[];
17
18
  editable: boolean;
18
19
 
19
- private editCallbacks: ((key: any, value: any) => void)[] = [];
20
+ private writeCallbacks: ((key: string, value: MultyxClientItem | undefined, oldValue: MultyxClientItem | undefined) => void)[] = [];
20
21
 
21
22
  get value() {
22
23
  const parsed: RawObject<MultyxClientItem> = {};
@@ -24,8 +25,8 @@ export default class MultyxClientObject {
24
25
  return parsed;
25
26
  }
26
27
 
27
- onWrite(callback: (key: any, value: any) => void) {
28
- this.editCallbacks.push(callback);
28
+ onWrite(callback: (key: string, value: MultyxClientItem | undefined, oldValue: MultyxClientItem | undefined) => void) {
29
+ this.writeCallbacks.push(callback);
29
30
  }
30
31
 
31
32
  [Edit](updatePath: string[], value: any) {
@@ -125,19 +126,21 @@ export default class MultyxClientObject {
125
126
  set(property: string | string[], value: any): boolean {
126
127
  if(Array.isArray(property)) return this.recursiveSet(property, value);
127
128
 
129
+ const oldValue = this.get(property);
130
+
128
131
  const serverSet = value instanceof EditWrapper;
129
132
  const allowed = serverSet || this.editable;
130
133
  const incoming = (serverSet || IsMultyxClientItem(value)) ? value.value : value;
131
134
  if(incoming === undefined) return this.delete(property, serverSet);
132
135
 
133
- if(serverSet && this.applyServerValue(property, incoming)) {
136
+ if(serverSet && this.tryApplyServerValue(property, incoming, oldValue)) {
134
137
  return true;
135
138
  }
136
139
 
137
140
  // Only create new MultyxClientItem when needed
138
141
  if(this.object[property] instanceof MultyxClientValue && (typeof incoming !== 'object' || incoming === null)) {
139
142
  const bool = this.object[property].set(serverSet ? new EditWrapper(incoming) : incoming);
140
- if(serverSet) this.editCallbacks.forEach(callback => callback(property, this.object[property]));
143
+ this.enqueueWriteCallbacks(property, oldValue);
141
144
  return bool;
142
145
  }
143
146
 
@@ -156,14 +159,17 @@ export default class MultyxClientObject {
156
159
  [...this.propertyPath, property],
157
160
  this.editable
158
161
  );
159
- if(serverSet) this.editCallbacks.forEach(callback => callback(property, this.object[property]));
160
162
 
161
163
  this.notifyPropertyWaiters(property);
164
+ this.enqueueWriteCallbacks(property, oldValue);
162
165
 
163
166
  return true;
164
167
  }
165
168
 
166
169
  delete(property: any, native: boolean = false) {
170
+ const key = typeof property === 'string' ? property : String(property);
171
+ const oldValue = this.get(key);
172
+
167
173
  // Attempting to edit property not editable by client
168
174
  if(!this.editable && !native) {
169
175
  if(this.multyx.options.verbose) {
@@ -173,7 +179,9 @@ export default class MultyxClientObject {
173
179
  }
174
180
 
175
181
  delete this.object[property];
176
- this.editCallbacks.forEach(callback => callback(property, undefined));
182
+ for(const listener of this.writeCallbacks) {
183
+ this.multyx[Add](() => listener(key, undefined, oldValue));
184
+ }
177
185
 
178
186
  if(!native) {
179
187
  this.multyx.ws.send(Message.Native({
@@ -235,26 +243,26 @@ export default class MultyxClientObject {
235
243
  }
236
244
  }
237
245
 
238
- private applyServerValue(property: string, incoming: any) {
246
+ private tryApplyServerValue(property: string, incoming: any, oldValue: MultyxClientItem | undefined) {
239
247
  const current = this.object[property];
240
248
  if(!current) return false;
241
249
 
242
250
  if(current instanceof MultyxClientValue && (typeof incoming !== 'object' || incoming === null)) {
243
251
  current.set(new EditWrapper(incoming));
244
- this.editCallbacks.forEach(callback => callback(property, current));
252
+ this.enqueueWriteCallbacks(property, oldValue);
245
253
  return true;
246
254
  }
247
255
 
248
256
  const canHydrate = typeof (current as any)?.hydrateFromServer === 'function';
249
257
  if(Array.isArray(incoming) && canHydrate && (current as any).type === 'list') {
250
258
  (current as any).hydrateFromServer(incoming);
251
- this.editCallbacks.forEach(callback => callback(property, current));
259
+ this.enqueueWriteCallbacks(property, oldValue);
252
260
  return true;
253
261
  }
254
262
 
255
263
  if(isPlainObject(incoming) && canHydrate && (current as any).type === 'object') {
256
264
  (current as any).hydrateFromServer(incoming);
257
- this.editCallbacks.forEach(callback => callback(property, current));
265
+ this.enqueueWriteCallbacks(property, oldValue);
258
266
  return true;
259
267
  }
260
268
 
@@ -269,4 +277,10 @@ export default class MultyxClientObject {
269
277
  ) ?? []));
270
278
  }
271
279
  }
280
+
281
+ private enqueueWriteCallbacks(property: string, oldValue: MultyxClientItem | undefined) {
282
+ for(const listener of this.writeCallbacks) {
283
+ this.multyx[Add](() => listener(property, this.get(property), oldValue));
284
+ }
285
+ }
272
286
  }