@latticexyz/recs 2.2.15 → 2.2.16-59389b1e37bc84664972231989ce7fdc739cce42
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/dist/{chunk-X4WOMEVS.js → chunk-2YR7WENB.js} +1 -1
- package/dist/{chunk-X4WOMEVS.js.map → chunk-2YR7WENB.js.map} +1 -1
- package/dist/deprecated/index.d.ts +2 -2
- package/dist/deprecated/index.js +1 -1
- package/dist/deprecated/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/{types-0972fad2.d.ts → types-CAQycjNT.d.ts} +1 -1
- package/package.json +3 -4
@@ -1,2 +1,2 @@
|
|
1
1
|
var P=(o=>(o[o.Boolean=0]="Boolean",o[o.Number=1]="Number",o[o.OptionalNumber=2]="OptionalNumber",o[o.BigInt=3]="BigInt",o[o.OptionalBigInt=4]="OptionalBigInt",o[o.String=5]="String",o[o.OptionalString=6]="OptionalString",o[o.NumberArray=7]="NumberArray",o[o.OptionalNumberArray=8]="OptionalNumberArray",o[o.BigIntArray=9]="BigIntArray",o[o.OptionalBigIntArray=10]="OptionalBigIntArray",o[o.StringArray=11]="StringArray",o[o.OptionalStringArray=12]="OptionalStringArray",o[o.Entity=13]="Entity",o[o.OptionalEntity=14]="OptionalEntity",o[o.EntityArray=15]="EntityArray",o[o.OptionalEntityArray=16]="OptionalEntityArray",o[o.T=17]="T",o[o.OptionalT=18]="OptionalT",o))(P||{}),k=(i=>(i[i.Enter=0]="Enter",i[i.Exit=1]="Exit",i[i.Update=2]="Update",i[i.Noop=3]="Noop",i))(k||{}),w=[14,16,2,8,4,10,6,12,18];import{transformIterator as j,uuid as F}from"@latticexyz/utils";import{mapObject as N}from"@latticexyz/utils";import{filter as L,map as D,Subject as $}from"rxjs";function M(e){let t=new Map;function n(l){let u=t.get(r(l));return u?new Set([...u].map(v)):new Set}function r(l){return Object.values(l).join("/")}function i(l,u){if(!u)return;let x=r(u),f=t.get(x);f||(f=new Set,t.set(x,f)),f.add(l)}function d(l,u){if(!u)return;let x=r(u),f=t.get(x);f&&f.delete(l)}for(let l of V(e)){let u=g(e,l);i(p(l),u)}let m=e.update$.subscribe(({entity:l,value:u})=>{d(p(l),u[1]),i(p(l),u[0])});return e.world.registerDisposer(()=>m?.unsubscribe()),{...e,getEntitiesWithValue:n}}import{map as W,pipe as B}from"rxjs";function X(e,t){return e.component===t}function U(e,t){let n=g(t,e);return{entity:e,component:t,value:[n,void 0],type:n==null?3:0}}function Y(e){return B(W(t=>U(t,e)))}function I(e){return"getEntitiesWithValue"in e}function T(e,t){return Object.keys(e.schema).every(n=>n in t)}function b(e){return e.metadata?.componentName??e.metadata?.tableName??e.metadata?.tableId??e.metadata?.contractId??e.id}function ie(e,t,n){if(Object.keys(t).length===0)throw new Error("Component schema must have at least one key");let r=n?.id??F(),i=N(t,()=>new Map),d=new $,m=n?.metadata,u={values:i,schema:t,id:r,update$:d,metadata:m,entities:()=>j(Object.values(i)[0].keys(),v),world:e};return n?.indexed&&(u=M(u)),e.registerComponent(u),u}function E(e,t,n,r={}){let i=p(t),d=g(e,t);for(let[m,l]of Object.entries(n))e.values[m]?e.values[m].set(i,l):e.metadata?.tableId&&/^\d+$/.test(m)||console.warn("Component definition for",b(e),"is missing key",m,", ignoring value",l,"for entity",t,". Existing keys: ",Object.keys(e.values));r.skipUpdateStream||e.update$.next({entity:t,value:[n,d],component:e})}function se(e,t,n,r,i={}){let d=g(e,t);if(d===void 0){if(r===void 0)throw new Error(`Can't update component ${b(e)} without a current value or initial value`);E(e,t,{...r,...n},i)}else E(e,t,{...d,...n},i)}function ue(e,t,n={}){let r=p(t),i=g(e,t);for(let d of Object.keys(e.values))e.values[d].delete(r);n.skipUpdateStream||e.update$.next({entity:t,value:[void 0,i],component:e})}function le(e,t){let n=p(t);return Object.values(e.values)[0].has(n)}function g(e,t){let n={},r=p(t),i=Object.keys(e.schema);for(let d of i){let m=e.values[d].get(r);if(m===void 0&&!w.includes(e.schema[d]))return;n[d]=m}return n}function de(e,t){let n=g(e,t);if(!n)throw new Error(`No value for component ${b(e)} on entity ${t}`);return n}function K(e,t){if(!e&&!t)return!0;if(!e||!t)return!1;let n=!0;for(let r of Object.keys(e))if(n=e[r]===t[r],!n)return!1;return n}function me(e,t){return[e,t]}function ce(e,t){if(I(e)&&T(e,t))return e.getEntitiesWithValue(t);let n=new Set;for(let r of V(e)){let i=g(e,r);K(t,i)&&n.add(r)}return n}function V(e){return e.entities()}function Se(e){let t=0,n=new Map,r=new Map,i=new $;function d(s,a){n.set(s,{update:a,nonce:t++}),C(a.entity,a.value)}function m(s){let a=n.get(s)?.update.entity;if(n.delete(s),a==null)return;let S=[...n.values()].filter(c=>c.update.entity===a).sort((c,h)=>c.nonce<h.nonce?-1:1);if(S.length>0){let c=S[S.length-1];C(a,c.update.value)}else C(a,void 0)}function l(s){let a=g(e,s),S=p(s),c=r.get(S);return(a||c)&&c!==null?{...a,...c}:void 0}let u=s=>({get(a,S){return S==="get"?c=>{let h=a.get(c),O=r.get(c);return O&&O[s]!=null?O[s]:h}:S==="has"?c=>a.has(c)||r.has(c):S==="keys"?()=>new Set([...a.keys(),...r.keys()]).values():Reflect.get(a,S,a)}}),x={};for(let s of Object.keys(e.values))x[s]=new Proxy(e.values[s],u(s));let f=x,y=new Proxy(e,{get(s,a){return a==="addOverride"?d:a==="removeOverride"?m:a==="values"?f:a==="update$"?i:a==="entities"?()=>new Set([...j(r.keys(),v),...s.entities()]).values():Reflect.get(s,a)},has(s,a){return a==="addOverride"||a==="removeOverride"?!0:a in s}});function C(s,a){let S=p(s),c=l(s);a!==void 0?r.set(S,a):r.delete(S),i.next({entity:s,value:[l(s),c],component:y})}return e.update$.pipe(L(s=>!r.get(p(s.entity))),D(s=>({...s,component:y}))).subscribe(i),y}function A(e,t){return`localcache-${t}-${e.id}`}function fe(e,t){localStorage.removeItem(A(e,t))}function pe(e,t){let{world:n,update$:r,values:i}=e,d=A(e,t),m=0,l=Date.now(),u=localStorage.getItem(d);if(u){let f=JSON.parse(u),y={};for(let[C,s]of f)for(let[a,S]of s)y[a]=y[a]||{},y[a][C]=S;for(let[C,s]of Object.entries(y)){let a=n.registerEntity({id:C});E(e,a,s)}console.info("Loading component",b(e),"from local cache.")}let x=r.subscribe(()=>{m++;let f=JSON.stringify(Object.entries(N(i,y=>[...y.entries()].map(C=>[v(C[0]),C[1]]))));localStorage.setItem(d,f),m>200&&console.warn("Component",b(e),"was locally cached",m,"times since",new Date(l).toLocaleTimeString(),"- the local cache is in an alpha state and should not be used with components that update frequently yet")});return e.world.registerDisposer(()=>x?.unsubscribe()),e}function ge(e,t,n){let r=e.registerEntity(n??{});if(t)for(let[i,d]of t)E(i,r,d);return r}function p(e){return Symbol.for(e)}function v(e){return Symbol.keyFor(e)}export{P as a,k as b,w as c,ge as d,p as e,v as f,M as g,X as h,U as i,Y as j,I as k,T as l,ie as m,E as n,se as o,ue as p,le as q,g as r,de as s,K as t,me as u,ce as v,V as w,Se as x,fe as y,pe as z};
|
2
|
-
//# sourceMappingURL=chunk-
|
2
|
+
//# sourceMappingURL=chunk-2YR7WENB.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/Component.ts","../src/Indexer.ts","../src/utils.ts","../src/Entity.ts"],"sourcesContent":["/**\n * Type enum is used to specify value types in {@link ComponentSchema} to be able\n * to access type values in JavaScript in addition to TypeScript type checks.\n */\nexport enum Type {\n Boolean,\n Number,\n OptionalNumber,\n BigInt,\n OptionalBigInt,\n String,\n OptionalString,\n NumberArray,\n OptionalNumberArray,\n BigIntArray,\n OptionalBigIntArray,\n StringArray,\n OptionalStringArray,\n Entity,\n OptionalEntity,\n EntityArray,\n OptionalEntityArray,\n T,\n OptionalT,\n}\n\n/**\n * Used to specify type of {@link ComponentUpdate}.\n * - Enter: Update added a value to an entity that did not have a value before\n * - Exit: Update removed a value from an entity that had a value before\n * - Update: Update changed a value of an entity that already had a value before. Note: the value doesn't need to be different from the previous value.\n * - Noop: Update did nothing (removed a value from an entity that did not have a value)\n */\nexport enum UpdateType {\n Enter,\n Exit,\n Update,\n Noop,\n}\n\n/**\n * Helper constant with all optional {@link Type}s.\n */\nexport const OptionalTypes = [\n Type.OptionalEntity,\n Type.OptionalEntityArray,\n Type.OptionalNumber,\n Type.OptionalNumberArray,\n Type.OptionalBigInt,\n Type.OptionalBigIntArray,\n Type.OptionalString,\n Type.OptionalStringArray,\n Type.OptionalT,\n];\n","import { transformIterator, uuid } from \"@latticexyz/utils\";\nimport { mapObject } from \"@latticexyz/utils\";\nimport { filter, map, Subject } from \"rxjs\";\nimport { OptionalTypes } from \"./constants\";\nimport { createIndexer } from \"./Indexer\";\nimport {\n Component,\n ComponentValue,\n Entity,\n EntitySymbol,\n Indexer,\n Metadata,\n OverridableComponent,\n Override,\n Schema,\n World,\n} from \"./types\";\nimport { isFullComponentValue, isIndexer } from \"./utils\";\nimport { getEntityString, getEntitySymbol } from \"./Entity\";\n\nexport type ComponentMutationOptions = {\n /** Skip publishing this mutation to the component's update stream. Mostly used internally during initial hydration. */\n skipUpdateStream?: boolean;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction getComponentName(component: Component<any, any, any>) {\n return (\n component.metadata?.componentName ??\n component.metadata?.tableName ??\n component.metadata?.tableId ??\n component.metadata?.contractId ??\n component.id\n );\n}\n\n/**\n * Components contain state indexed by entities and are one of the fundamental building blocks in ECS.\n * Besides containing the state, components expose an rxjs update$ stream, that emits an event any time the value\n * of an entity in this component is updated.\n *\n * @param world {@link World} object this component should be registered onto.\n * @param schema {@link Schema} of component values. Uses Type enum as bridge between typescript types and javascript accessible values.\n * @param options Optional: {\n * id: descriptive id for this component (otherwise an autogenerated id is used),\n * metadata: arbitrary metadata,\n * indexed: if this flag is set, an indexer is applied to this component (see {@link createIndexer})\n * }\n * @returns Component object linked to the provided World\n *\n * @example\n * ```\n * const Position = defineComponent(world, { x: Type.Number, y: Type.Number }, { id: \"Position\" });\n * ```\n */\nexport function defineComponent<S extends Schema, M extends Metadata, T = unknown>(\n world: World,\n schema: S,\n options?: { id?: string; metadata?: M; indexed?: boolean },\n) {\n if (Object.keys(schema).length === 0) throw new Error(\"Component schema must have at least one key\");\n const id = options?.id ?? uuid();\n const values = mapObject(schema, () => new Map());\n const update$ = new Subject();\n const metadata = options?.metadata;\n const entities = () =>\n transformIterator((Object.values(values)[0] as Map<EntitySymbol, unknown>).keys(), getEntityString);\n let component = { values, schema, id, update$, metadata, entities, world } as Component<S, M, T>;\n if (options?.indexed) component = createIndexer(component);\n world.registerComponent(component as Component);\n return component;\n}\n\n/**\n * Set the value for a given entity in a given component.\n *\n * @param component {@link defineComponent Component} to be updated.\n * @param entity {@link Entity} whose value in the given component should be set.\n * @param value Value to set, schema must match the component schema.\n *\n * @example\n * ```\n * setComponent(Position, entity, { x: 1, y: 2 });\n * ```\n */\nexport function setComponent<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n entity: Entity,\n value: ComponentValue<S, T>,\n options: ComponentMutationOptions = {},\n) {\n const entitySymbol = getEntitySymbol(entity);\n const prevValue = getComponentValue(component, entity);\n for (const [key, val] of Object.entries(value)) {\n if (component.values[key]) {\n component.values[key].set(entitySymbol, val);\n } else {\n const isTableFieldIndex = component.metadata?.tableId && /^\\d+$/.test(key);\n if (!isTableFieldIndex) {\n // If this key looks like a field index from `defineStoreComponents`,\n // we can ignore this value without logging anything.\n //\n // Otherwise, we should let the user know we found undefined data.\n console.warn(\n \"Component definition for\",\n getComponentName(component),\n \"is missing key\",\n key,\n \", ignoring value\",\n val,\n \"for entity\",\n entity,\n \". Existing keys: \",\n Object.keys(component.values),\n );\n }\n }\n }\n if (!options.skipUpdateStream) {\n component.update$.next({ entity, value: [value, prevValue], component });\n }\n}\n\n/**\n * Update the value for a given entity in a given component while keeping the old value of keys not included in the update.\n *\n * @param component {@link defineComponent Component} to be updated.\n * @param entity {@link Entity} whose value in the given component should be updated.\n * @param value Partial value to be set, remaining keys will be taken from the existing component value.\n *\n * @remarks\n * This function fails silently during runtime if a partial value is set for an entity that\n * does not have a component value yet, since then a partial value will be set in the component for this entity.\n *\n * @example\n * ```\n * updateComponent(Position, entity, { x: 1 });\n * ```\n */\nexport function updateComponent<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n entity: Entity,\n value: Partial<ComponentValue<S, T>>,\n initialValue?: ComponentValue<S, T>,\n options: ComponentMutationOptions = {},\n) {\n const currentValue = getComponentValue(component, entity);\n if (currentValue === undefined) {\n if (initialValue === undefined) {\n throw new Error(`Can't update component ${getComponentName(component)} without a current value or initial value`);\n }\n setComponent(component, entity, { ...initialValue, ...value }, options);\n } else {\n setComponent(component, entity, { ...currentValue, ...value }, options);\n }\n}\n\n/**\n * Remove a given entity from a given component.\n *\n * @param component {@link defineComponent Component} to be updated.\n * @param entity {@link Entity} whose value should be removed from this component.\n */\nexport function removeComponent<S extends Schema, M extends Metadata, T = unknown>(\n component: Component<S, M, T>,\n entity: Entity,\n options: ComponentMutationOptions = {},\n) {\n const entitySymbol = getEntitySymbol(entity);\n const prevValue = getComponentValue(component, entity);\n for (const key of Object.keys(component.values)) {\n component.values[key].delete(entitySymbol);\n }\n if (!options.skipUpdateStream) {\n component.update$.next({ entity, value: [undefined, prevValue], component });\n }\n}\n\n/**\n * Check whether a component contains a value for a given entity.\n *\n * @param component {@link defineComponent Component} to check whether it has a value for the given entity.\n * @param entity {@link Entity} to check whether it has a value in the given component.\n * @returns true if the component contains a value for the given entity, else false.\n */\nexport function hasComponent<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n entity: Entity,\n): boolean {\n const entitySymbol = getEntitySymbol(entity);\n const map = Object.values(component.values)[0];\n return map.has(entitySymbol);\n}\n\n/**\n * Get the value of a given entity in the given component.\n * Returns undefined if no value or only a partial value is found.\n *\n * @param component {@link defineComponent Component} to get the value from for the given entity.\n * @param entity {@link Entity} to get the value for from the given component.\n * @returns Value of the given entity in the given component or undefined if no value exists.\n */\nexport function getComponentValue<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n entity: Entity,\n): ComponentValue<S, T> | undefined {\n const value: Record<string, unknown> = {};\n const entitySymbol = getEntitySymbol(entity);\n\n // Get the value of each schema key\n const schemaKeys = Object.keys(component.schema);\n for (const key of schemaKeys) {\n const val = component.values[key].get(entitySymbol);\n if (val === undefined && !OptionalTypes.includes(component.schema[key])) return undefined;\n value[key] = val;\n }\n\n return value as ComponentValue<S, T>;\n}\n\n/**\n * Get the value of a given entity in the given component.\n * Throws an error if no value exists for the given entity in the given component.\n *\n * @param component {@link defineComponent Component} to get the value from for the given entity.\n * @param entity {@link Entity} of the entity to get the value for from the given component.\n * @returns Value of the given entity in the given component.\n *\n * @remarks\n * Throws an error if no value exists in the component for the given entity.\n */\nexport function getComponentValueStrict<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n entity: Entity,\n): ComponentValue<S, T> {\n const value = getComponentValue(component, entity);\n if (!value) throw new Error(`No value for component ${getComponentName(component)} on entity ${entity}`);\n return value;\n}\n\n/**\n * Compare two {@link ComponentValue}s.\n * `a` can be a partial component value, in which case only the keys present in `a` are compared to the corresponding keys in `b`.\n *\n * @param a Partial {@link ComponentValue} to compare to `b`\n * @param b Component value to compare `a` to.\n * @returns True if `a` equals `b` in the keys present in a or neither `a` nor `b` are defined, else false.\n *\n * @example\n * ```\n * componentValueEquals({ x: 1, y: 2 }, { x: 1, y: 3 }) // returns false because value of y doesn't match\n * componentValueEquals({ x: 1 }, { x: 1, y: 3 }) // returns true because x is equal and y is not present in a\n * ```\n */\nexport function componentValueEquals<S extends Schema, T = unknown>(\n a?: Partial<ComponentValue<S, T>>,\n b?: ComponentValue<S, T>,\n): boolean {\n if (!a && !b) return true;\n if (!a || !b) return false;\n\n let equals = true;\n for (const key of Object.keys(a)) {\n equals = a[key] === b[key];\n if (!equals) return false;\n }\n return equals;\n}\n\n/**\n * Util to create a tuple of a component and value with matching schema.\n * (Used to enforce Typescript type safety.)\n *\n * @param component {@link defineComponent Component} with {@link ComponentSchema} `S`\n * @param value {@link ComponentValue} with {@link ComponentSchema} `S`\n * @returns Tuple `[component, value]`\n */\nexport function withValue<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n value: ComponentValue<S, T>,\n): [Component<S, Metadata, T>, ComponentValue<S, T>] {\n return [component, value];\n}\n\n/**\n * Get a set of entities that have the given component value in the given component.\n *\n * @param component {@link defineComponent Component} to get entities with the given value from.\n * @param value look for entities with this {@link ComponentValue}.\n * @returns Set with {@link Entity Entities} with the given component value.\n */\nexport function getEntitiesWithValue<S extends Schema>(\n component: Component<S> | Indexer<S>,\n value: Partial<ComponentValue<S>>,\n): Set<Entity> {\n // Shortcut for indexers\n if (isIndexer(component) && isFullComponentValue(component, value)) {\n return component.getEntitiesWithValue(value);\n }\n\n // Trivial implementation for regular components\n const entities = new Set<Entity>();\n for (const entity of getComponentEntities(component)) {\n const val = getComponentValue(component, entity);\n if (componentValueEquals(value, val)) {\n entities.add(entity);\n }\n }\n return entities;\n}\n\n/**\n * Get a set of all entities of the given component.\n *\n * @param component {@link defineComponent Component} to get all entities from\n * @returns Set of all entities in the given component.\n */\nexport function getComponentEntities<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n): IterableIterator<Entity> {\n return component.entities();\n}\n\n/**\n * An overridable component is a mirror of the source component, with functions to lazily override specific entity values.\n * Lazily override means the values are not actually set to the source component, but the override is only returned if the value is read.\n *\n * - When an override for an entity is added to the component, the override is propagated via the component's `update$` stream.\n * - While an override is set for a specific entity, no updates to the source component for this entity will be propagated to the `update$` stream.\n * - When an override is removed for a specific entity and there are more overrides targeting this entity,\n * the override with the highest nonce will be propagated to the `update$` stream.\n * - When an override is removed for a specific entity and there are no more overrides targeting this entity,\n * the non-overridden underlying component value of this entity will be propagated to the `update$` stream.\n *\n * @param component {@link defineComponent Component} to use as underlying source for the overridable component\n * @returns overridable component\n */\nexport function overridableComponent<S extends Schema, M extends Metadata, T = unknown>(\n component: Component<S, M, T>,\n): OverridableComponent<S, M, T> {\n let nonce = 0;\n\n // Map from OverrideId to Override (to be able to add multiple overrides to the same Entity)\n const overrides = new Map<string, { update: Override<S, T>; nonce: number }>();\n\n // Map from EntitySymbol to current overridden component value\n const overriddenEntityValues = new Map<EntitySymbol, Partial<ComponentValue<S, T>> | null>();\n\n // Update event stream that takes into account overridden entity values\n const update$ = new Subject<{\n entity: Entity;\n value: [ComponentValue<S, T> | undefined, ComponentValue<S, T> | undefined];\n component: Component<S, Metadata, T>;\n }>();\n\n // Add a new override to some entity\n function addOverride(id: string, update: Override<S, T>) {\n overrides.set(id, { update, nonce: nonce++ });\n setOverriddenComponentValue(update.entity, update.value);\n }\n\n // Remove an override from an entity\n function removeOverride(id: string) {\n const affectedEntity = overrides.get(id)?.update.entity;\n overrides.delete(id);\n\n if (affectedEntity == null) return;\n\n // If there are more overries affecting this entity,\n // set the overriddenEntityValue to the last override\n const relevantOverrides = [...overrides.values()]\n .filter((o) => o.update.entity === affectedEntity)\n .sort((a, b) => (a.nonce < b.nonce ? -1 : 1));\n\n if (relevantOverrides.length > 0) {\n const lastOverride = relevantOverrides[relevantOverrides.length - 1];\n setOverriddenComponentValue(affectedEntity, lastOverride.update.value);\n } else {\n setOverriddenComponentValue(affectedEntity, undefined);\n }\n }\n\n // Internal function to get the current overridden value or value of the source component\n function getOverriddenComponentValue(entity: Entity): ComponentValue<S, T> | undefined {\n const originalValue = getComponentValue(component, entity);\n const entitySymbol = getEntitySymbol(entity);\n const overriddenValue = overriddenEntityValues.get(entitySymbol);\n return (originalValue || overriddenValue) && overriddenValue !== null // null is a valid override, in this case return undefined\n ? ({ ...originalValue, ...overriddenValue } as ComponentValue<S, T>)\n : undefined;\n }\n\n const valueProxyHandler: (key: keyof S) => ProxyHandler<(typeof component.values)[typeof key]> = (key: keyof S) => ({\n get(target, prop) {\n // Intercept calls to component.value[key].get(entity)\n if (prop === \"get\") {\n return (entity: EntitySymbol) => {\n const originalValue = target.get(entity);\n const overriddenValue = overriddenEntityValues.get(entity);\n return overriddenValue && overriddenValue[key] != null ? overriddenValue[key] : originalValue;\n };\n }\n\n // Intercept calls to component.value[key].has(entity)\n if (prop === \"has\") {\n return (entity: EntitySymbol) => {\n return target.has(entity) || overriddenEntityValues.has(entity);\n };\n }\n\n // Intercept calls to component.value[key].keys()\n if (prop === \"keys\") {\n return () => new Set([...target.keys(), ...overriddenEntityValues.keys()]).values();\n }\n\n return Reflect.get(target, prop, target);\n },\n });\n\n const partialValues: Partial<Component<S, M, T>[\"values\"]> = {};\n for (const key of Object.keys(component.values) as (keyof S)[]) {\n partialValues[key] = new Proxy(component.values[key], valueProxyHandler(key));\n }\n const valuesProxy = partialValues as Component<S, M, T>[\"values\"];\n\n const overriddenComponent = new Proxy(component, {\n get(target, prop) {\n if (prop === \"addOverride\") return addOverride;\n if (prop === \"removeOverride\") return removeOverride;\n if (prop === \"values\") return valuesProxy;\n if (prop === \"update$\") return update$;\n if (prop === \"entities\")\n return () =>\n new Set([\n ...transformIterator(overriddenEntityValues.keys(), getEntityString),\n ...target.entities(),\n ]).values();\n\n return Reflect.get(target, prop);\n },\n has(target, prop) {\n if (prop === \"addOverride\" || prop === \"removeOverride\") return true;\n return prop in target;\n },\n }) as OverridableComponent<S, M, T>;\n\n // Internal function to set the current overridden component value and emit the update event\n function setOverriddenComponentValue(entity: Entity, value?: Partial<ComponentValue<S, T>> | null) {\n const entitySymbol = getEntitySymbol(entity);\n // Check specifically for undefined - null is a valid override\n const prevValue = getOverriddenComponentValue(entity);\n if (value !== undefined) overriddenEntityValues.set(entitySymbol, value);\n else overriddenEntityValues.delete(entitySymbol);\n update$.next({ entity, value: [getOverriddenComponentValue(entity), prevValue], component: overriddenComponent });\n }\n\n // Channel through update events from the original component if there are no overrides\n component.update$\n .pipe(\n filter((e) => !overriddenEntityValues.get(getEntitySymbol(e.entity))),\n map((update) => ({ ...update, component: overriddenComponent })),\n )\n .subscribe(update$);\n\n return overriddenComponent;\n}\n\nfunction getLocalCacheId(component: Component, uniqueWorldIdentifier?: string): string {\n return `localcache-${uniqueWorldIdentifier}-${component.id}`;\n}\n\nexport function clearLocalCache(component: Component, uniqueWorldIdentifier?: string): void {\n localStorage.removeItem(getLocalCacheId(component, uniqueWorldIdentifier));\n}\n\n// Note: Only proof of concept for now - use this only for component that do not update frequently\nexport function createLocalCache<S extends Schema, M extends Metadata, T = unknown>(\n component: Component<S, M, T>,\n uniqueWorldIdentifier?: string,\n): Component<S, M, T> {\n const { world, update$, values } = component;\n const cacheId = getLocalCacheId(component as Component, uniqueWorldIdentifier);\n let numUpdates = 0;\n const creation = Date.now();\n\n // On creation, check if this component has locally cached values\n const encodedCache = localStorage.getItem(cacheId);\n if (encodedCache) {\n const cache = JSON.parse(encodedCache) as [string, [Entity, unknown][]][];\n const state: { [entity: Entity]: { [key: string]: unknown } } = {};\n\n for (const [key, values] of cache) {\n for (const [entity, value] of values) {\n state[entity] = state[entity] || {};\n state[entity][key] = value;\n }\n }\n\n for (const [entityId, value] of Object.entries(state)) {\n const entity = world.registerEntity({ id: entityId });\n setComponent(component, entity, value as ComponentValue<S, T>);\n }\n\n console.info(\"Loading component\", getComponentName(component), \"from local cache.\");\n }\n\n // Flush the entire component to the local cache every time it updates.\n // Note: this is highly unperformant and should only be used for components that\n // don't update often and don't have many values\n const updateSub = update$.subscribe(() => {\n numUpdates++;\n const encoded = JSON.stringify(\n Object.entries(mapObject(values, (m) => [...m.entries()].map((e) => [getEntityString(e[0]), e[1]]))),\n );\n localStorage.setItem(cacheId, encoded);\n if (numUpdates > 200) {\n console.warn(\n \"Component\",\n getComponentName(component),\n \"was locally cached\",\n numUpdates,\n \"times since\",\n new Date(creation).toLocaleTimeString(),\n \"- the local cache is in an alpha state and should not be used with components that update frequently yet\",\n );\n }\n });\n component.world.registerDisposer(() => updateSub?.unsubscribe());\n\n return component;\n}\n","import { getComponentEntities, getComponentValue } from \"./Component\";\nimport { getEntityString, getEntitySymbol } from \"./Entity\";\nimport { Component, ComponentValue, Entity, EntitySymbol, Indexer, Metadata, Schema } from \"./types\";\n\n/**\n * Create an indexed component from a given component.\n *\n * @remarks\n * An indexed component keeps a \"reverse mapping\" from {@link ComponentValue} to the Set of {@link createEntity Entities} with this value.\n * This adds a performance overhead to modifying component values and a memory overhead since in the worst case there is one\n * Set per entity (if every entity has a different component value).\n * In return the performance for querying for entities with a given component value is close to O(1) (instead of O(#entities) in a regular non-indexed component).\n * As a rule of thumb only components that are added to many entities and are queried with {@link HasValue} a lot should be indexed (eg. the Position component).\n *\n * @dev This could be made more (memory) efficient by using a hash of the component value as key, but would require handling hash collisions.\n *\n * @param component {@link defineComponent Component} to index.\n * @returns Indexed version of the component.\n */\nexport function createIndexer<S extends Schema, M extends Metadata, T = unknown>(\n component: Component<S, M, T>,\n): Indexer<S, M, T> {\n const valueToEntities = new Map<string, Set<EntitySymbol>>();\n\n function getEntitiesWithValue(value: ComponentValue<S, T>) {\n const entities = valueToEntities.get(getValueKey(value));\n return entities ? new Set([...entities].map(getEntityString)) : new Set<Entity>();\n }\n\n function getValueKey(value: ComponentValue<S, T>): string {\n return Object.values(value).join(\"/\");\n }\n\n function add(entity: EntitySymbol, value: ComponentValue<S, T> | undefined) {\n if (!value) return;\n const valueKey = getValueKey(value);\n let entitiesWithValue = valueToEntities.get(valueKey);\n if (!entitiesWithValue) {\n entitiesWithValue = new Set<EntitySymbol>();\n valueToEntities.set(valueKey, entitiesWithValue);\n }\n entitiesWithValue.add(entity);\n }\n\n function remove(entity: EntitySymbol, value: ComponentValue<S, T> | undefined) {\n if (!value) return;\n const valueKey = getValueKey(value);\n const entitiesWithValue = valueToEntities.get(valueKey);\n if (!entitiesWithValue) return;\n entitiesWithValue.delete(entity);\n }\n\n // Initial indexing\n for (const entity of getComponentEntities(component)) {\n const value = getComponentValue(component, entity);\n add(getEntitySymbol(entity), value);\n }\n\n // Keeping index up to date\n const subscription = component.update$.subscribe(({ entity, value }) => {\n // Remove from previous location\n remove(getEntitySymbol(entity), value[1]);\n\n // Add to new location\n add(getEntitySymbol(entity), value[0]);\n });\n\n component.world.registerDisposer(() => subscription?.unsubscribe());\n\n return { ...component, getEntitiesWithValue };\n}\n","import { map, pipe } from \"rxjs\";\nimport { getComponentValue } from \"./Component\";\nimport { UpdateType } from \"./constants\";\nimport { Component, ComponentUpdate, ComponentValue, Entity, Indexer, Schema } from \"./types\";\n\n/**\n * Type guard to infer the TypeScript type of a given component update\n *\n * @param update Component update to infer the type of.\n * @param component {@link defineComponent Component} to check whether the given update corresponds to it.\n * @returns True (+ infered type for `update`) if `update` belongs to `component`. Else false.\n */\nexport function isComponentUpdate<S extends Schema>(\n update: ComponentUpdate,\n component: Component<S>,\n): update is ComponentUpdate<S> {\n return update.component === component;\n}\n\n/**\n * Helper function to create a component update for the current component value of a given entity.\n *\n * @param entity Entity to create the component update for.\n * @param component Component to create the component update for.\n * @returns Component update corresponding to the given entity, the given component and the entity's current component value.\n */\nexport function toUpdate<S extends Schema>(entity: Entity, component: Component<S>) {\n const value = getComponentValue(component, entity);\n return {\n entity,\n component,\n value: [value, undefined],\n type: value == null ? UpdateType.Noop : UpdateType.Enter,\n } as ComponentUpdate<S> & {\n type: UpdateType;\n };\n}\n\n/**\n * Helper function to turn a stream of {@link Entity Entities} into a stream of component updates of the given component.\n * @param component Component to create update stream for.\n * @returns Unary function to be used with RxJS that turns stream of {@link Entity Entities} into stream of component updates.\n */\nexport function toUpdateStream<S extends Schema>(component: Component<S>) {\n return pipe(map((entity: Entity) => toUpdate(entity, component)));\n}\n\n/**\n * Helper function to check whether a given component is indexed.\n * @param c\n * @returns\n */\nexport function isIndexer<S extends Schema>(c: Component<S> | Indexer<S>): c is Indexer<S> {\n return \"getEntitiesWithValue\" in c;\n}\n\n/**\n * Helper function to check whether a given component value is partial or full.\n * @param component\n * @param value\n * @returns\n */\nexport function isFullComponentValue<S extends Schema>(\n component: Component<S>,\n value: Partial<ComponentValue<S>>,\n): value is ComponentValue<S> {\n return Object.keys(component.schema).every((key) => key in value);\n}\n","import { setComponent } from \"./Component\";\nimport { Component, ComponentValue, Entity, EntitySymbol, World } from \"./types\";\n\n/**\n * Register a new entity in the given {@link World} and initialize it with the given {@link ComponentValue}s.\n *\n * @param world World object this entity should be registered in.\n * @param components Array of [{@link defineComponent Component}, {@link ComponentValue}] tuples to be added to this entity.\n * (Use {@link withValue} to generate these tuples with type safety.)\n * @param options Optional: {\n * id: {@link Entity} for this entity. Use this for entities that were created outside of recs.\n * idSuffix: string to be appended to the auto-generated id. Use this for improved readability. Do not use this if the `id` option is provided.\n * }\n * @returns index of this entity in the {@link World}. This {@link Entity} is used to refer to this entity in other recs methods (eg {@link setComponent}).\n * (This is to avoid having to store strings in every component.)\n */\nexport function createEntity(\n world: World,\n components?: [Component, ComponentValue][],\n options?: { id?: string } | { idSuffix?: string },\n): Entity {\n const entity = world.registerEntity(options ?? {});\n\n if (components) {\n for (const [component, value] of components) {\n setComponent(component, entity, value);\n }\n }\n\n return entity;\n}\n\n/*\n * Get the symbol corresponding to an entity's string ID.\n * Entities are represented as symbols internally for memory efficiency.\n */\nexport function getEntitySymbol(entityString: string): EntitySymbol {\n return Symbol.for(entityString) as EntitySymbol;\n}\n\n/**\n * Get the underlying entity string of an entity symbol.\n */\nexport function getEntityString(entity: EntitySymbol): Entity {\n return Symbol.keyFor(entity) as Entity;\n}\n"],"mappings":"AAIO,IAAKA,OACVA,IAAA,qBACAA,IAAA,mBACAA,IAAA,mCACAA,IAAA,mBACAA,IAAA,mCACAA,IAAA,mBACAA,IAAA,mCACAA,IAAA,6BACAA,IAAA,6CACAA,IAAA,6BACAA,IAAA,8CACAA,IAAA,8BACAA,IAAA,8CACAA,IAAA,oBACAA,IAAA,oCACAA,IAAA,8BACAA,IAAA,8CACAA,IAAA,UACAA,IAAA,0BAnBUA,OAAA,IA6BAC,OACVA,IAAA,iBACAA,IAAA,eACAA,IAAA,mBACAA,IAAA,eAJUA,OAAA,IAUCC,EAAgB,CAC3B,GACA,GACA,EACA,EACA,EACA,GACA,EACA,GACA,EACF,ECrDA,OAAS,qBAAAC,EAAmB,QAAAC,MAAY,oBACxC,OAAS,aAAAC,MAAiB,oBAC1B,OAAS,UAAAC,EAAQ,OAAAC,EAAK,WAAAC,MAAe,OCiB9B,SAASC,EACdC,EACkB,CAClB,IAAMC,EAAkB,IAAI,IAE5B,SAASC,EAAqBC,EAA6B,CACzD,IAAMC,EAAWH,EAAgB,IAAII,EAAYF,CAAK,CAAC,EACvD,OAAOC,EAAW,IAAI,IAAI,CAAC,GAAGA,CAAQ,EAAE,IAAIE,CAAe,CAAC,EAAI,IAAI,GACtE,CAEA,SAASD,EAAYF,EAAqC,CACxD,OAAO,OAAO,OAAOA,CAAK,EAAE,KAAK,GAAG,CACtC,CAEA,SAASI,EAAIC,EAAsBL,EAAyC,CAC1E,GAAI,CAACA,EAAO,OACZ,IAAMM,EAAWJ,EAAYF,CAAK,EAC9BO,EAAoBT,EAAgB,IAAIQ,CAAQ,EAC/CC,IACHA,EAAoB,IAAI,IACxBT,EAAgB,IAAIQ,EAAUC,CAAiB,GAEjDA,EAAkB,IAAIF,CAAM,CAC9B,CAEA,SAASG,EAAOH,EAAsBL,EAAyC,CAC7E,GAAI,CAACA,EAAO,OACZ,IAAMM,EAAWJ,EAAYF,CAAK,EAC5BO,EAAoBT,EAAgB,IAAIQ,CAAQ,EACjDC,GACLA,EAAkB,OAAOF,CAAM,CACjC,CAGA,QAAWA,KAAUI,EAAqBZ,CAAS,EAAG,CACpD,IAAMG,EAAQU,EAAkBb,EAAWQ,CAAM,EACjDD,EAAIO,EAAgBN,CAAM,EAAGL,CAAK,EAIpC,IAAMY,EAAef,EAAU,QAAQ,UAAU,CAAC,CAAE,OAAAQ,EAAQ,MAAAL,CAAM,IAAM,CAEtEQ,EAAOG,EAAgBN,CAAM,EAAGL,EAAM,CAAC,CAAC,EAGxCI,EAAIO,EAAgBN,CAAM,EAAGL,EAAM,CAAC,CAAC,CACvC,CAAC,EAED,OAAAH,EAAU,MAAM,iBAAiB,IAAMe,GAAc,YAAY,CAAC,EAE3D,CAAE,GAAGf,EAAW,qBAAAE,CAAqB,CAC9C,CCtEA,OAAS,OAAAc,EAAK,QAAAC,MAAY,OAYnB,SAASC,EACdC,EACAC,EAC8B,CAC9B,OAAOD,EAAO,YAAcC,CAC9B,CASO,SAASC,EAA2BC,EAAgBF,EAAyB,CAClF,IAAMG,EAAQC,EAAkBJ,EAAWE,CAAM,EACjD,MAAO,CACL,OAAAA,EACA,UAAAF,EACA,MAAO,CAACG,EAAO,MAAS,EACxB,KAAMA,GAAS,QACjB,CAGF,CAOO,SAASE,EAAiCL,EAAyB,CACxE,OAAOM,EAAKC,EAAKL,GAAmBD,EAASC,EAAQF,CAAS,CAAC,CAAC,CAClE,CAOO,SAASQ,EAA4BC,EAA+C,CACzF,MAAO,yBAA0BA,CACnC,CAQO,SAASC,EACdV,EACAG,EAC4B,CAC5B,OAAO,OAAO,KAAKH,EAAU,MAAM,EAAE,MAAOW,GAAQA,KAAOR,CAAK,CAClE,CFzCA,SAASS,EAAiBC,EAAqC,CAC7D,OACEA,EAAU,UAAU,eACpBA,EAAU,UAAU,WACpBA,EAAU,UAAU,SACpBA,EAAU,UAAU,YACpBA,EAAU,EAEd,CAqBO,SAASC,GACdC,EACAC,EACAC,EACA,CACA,GAAI,OAAO,KAAKD,CAAM,EAAE,SAAW,EAAG,MAAM,IAAI,MAAM,6CAA6C,EACnG,IAAME,EAAKD,GAAS,IAAME,EAAK,EACzBC,EAASC,EAAUL,EAAQ,IAAM,IAAI,GAAK,EAC1CM,EAAU,IAAIC,EACdC,EAAWP,GAAS,SAGtBJ,EAAY,CAAE,OAAAO,EAAQ,OAAAJ,EAAQ,GAAAE,EAAI,QAAAI,EAAS,SAAAE,EAAU,SAFxC,IACfC,EAAmB,OAAO,OAAOL,CAAM,EAAE,CAAC,EAAiC,KAAK,EAAGM,CAAe,EACjC,MAAAX,CAAM,EACzE,OAAIE,GAAS,UAASJ,EAAYc,EAAcd,CAAS,GACzDE,EAAM,kBAAkBF,CAAsB,EACvCA,CACT,CAcO,SAASe,EACdf,EACAgB,EACAC,EACAb,EAAoC,CAAC,EACrC,CACA,IAAMc,EAAeC,EAAgBH,CAAM,EACrCI,EAAYC,EAAkBrB,EAAWgB,CAAM,EACrD,OAAW,CAACM,EAAKC,CAAG,IAAK,OAAO,QAAQN,CAAK,EACvCjB,EAAU,OAAOsB,CAAG,EACtBtB,EAAU,OAAOsB,CAAG,EAAE,IAAIJ,EAAcK,CAAG,EAEjBvB,EAAU,UAAU,SAAW,QAAQ,KAAKsB,CAAG,GAMvE,QAAQ,KACN,2BACAvB,EAAiBC,CAAS,EAC1B,iBACAsB,EACA,mBACAC,EACA,aACAP,EACA,oBACA,OAAO,KAAKhB,EAAU,MAAM,CAC9B,EAIDI,EAAQ,kBACXJ,EAAU,QAAQ,KAAK,CAAE,OAAAgB,EAAQ,MAAO,CAACC,EAAOG,CAAS,EAAG,UAAApB,CAAU,CAAC,CAE3E,CAkBO,SAASwB,GACdxB,EACAgB,EACAC,EACAQ,EACArB,EAAoC,CAAC,EACrC,CACA,IAAMsB,EAAeL,EAAkBrB,EAAWgB,CAAM,EACxD,GAAIU,IAAiB,OAAW,CAC9B,GAAID,IAAiB,OACnB,MAAM,IAAI,MAAM,0BAA0B1B,EAAiBC,CAAS,4CAA4C,EAElHe,EAAaf,EAAWgB,EAAQ,CAAE,GAAGS,EAAc,GAAGR,CAAM,EAAGb,CAAO,OAEtEW,EAAaf,EAAWgB,EAAQ,CAAE,GAAGU,EAAc,GAAGT,CAAM,EAAGb,CAAO,CAE1E,CAQO,SAASuB,GACd3B,EACAgB,EACAZ,EAAoC,CAAC,EACrC,CACA,IAAMc,EAAeC,EAAgBH,CAAM,EACrCI,EAAYC,EAAkBrB,EAAWgB,CAAM,EACrD,QAAWM,KAAO,OAAO,KAAKtB,EAAU,MAAM,EAC5CA,EAAU,OAAOsB,CAAG,EAAE,OAAOJ,CAAY,EAEtCd,EAAQ,kBACXJ,EAAU,QAAQ,KAAK,CAAE,OAAAgB,EAAQ,MAAO,CAAC,OAAWI,CAAS,EAAG,UAAApB,CAAU,CAAC,CAE/E,CASO,SAAS4B,GACd5B,EACAgB,EACS,CACT,IAAME,EAAeC,EAAgBH,CAAM,EAE3C,OADY,OAAO,OAAOhB,EAAU,MAAM,EAAE,CAAC,EAClC,IAAIkB,CAAY,CAC7B,CAUO,SAASG,EACdrB,EACAgB,EACkC,CAClC,IAAMC,EAAiC,CAAC,EAClCC,EAAeC,EAAgBH,CAAM,EAGrCa,EAAa,OAAO,KAAK7B,EAAU,MAAM,EAC/C,QAAWsB,KAAOO,EAAY,CAC5B,IAAMN,EAAMvB,EAAU,OAAOsB,CAAG,EAAE,IAAIJ,CAAY,EAClD,GAAIK,IAAQ,QAAa,CAACO,EAAc,SAAS9B,EAAU,OAAOsB,CAAG,CAAC,EAAG,OACzEL,EAAMK,CAAG,EAAIC,EAGf,OAAON,CACT,CAaO,SAASc,GACd/B,EACAgB,EACsB,CACtB,IAAMC,EAAQI,EAAkBrB,EAAWgB,CAAM,EACjD,GAAI,CAACC,EAAO,MAAM,IAAI,MAAM,0BAA0BlB,EAAiBC,CAAS,eAAegB,GAAQ,EACvG,OAAOC,CACT,CAgBO,SAASe,EACdC,EACAC,EACS,CACT,GAAI,CAACD,GAAK,CAACC,EAAG,MAAO,GACrB,GAAI,CAACD,GAAK,CAACC,EAAG,MAAO,GAErB,IAAIC,EAAS,GACb,QAAWb,KAAO,OAAO,KAAKW,CAAC,EAE7B,GADAE,EAASF,EAAEX,CAAG,IAAMY,EAAEZ,CAAG,EACrB,CAACa,EAAQ,MAAO,GAEtB,OAAOA,CACT,CAUO,SAASC,GACdpC,EACAiB,EACmD,CACnD,MAAO,CAACjB,EAAWiB,CAAK,CAC1B,CASO,SAASoB,GACdrC,EACAiB,EACa,CAEb,GAAIqB,EAAUtC,CAAS,GAAKuC,EAAqBvC,EAAWiB,CAAK,EAC/D,OAAOjB,EAAU,qBAAqBiB,CAAK,EAI7C,IAAMuB,EAAW,IAAI,IACrB,QAAWxB,KAAUyB,EAAqBzC,CAAS,EAAG,CACpD,IAAMuB,EAAMF,EAAkBrB,EAAWgB,CAAM,EAC3CgB,EAAqBf,EAAOM,CAAG,GACjCiB,EAAS,IAAIxB,CAAM,EAGvB,OAAOwB,CACT,CAQO,SAASC,EACdzC,EAC0B,CAC1B,OAAOA,EAAU,SAAS,CAC5B,CAgBO,SAAS0C,GACd1C,EAC+B,CAC/B,IAAI2C,EAAQ,EAGNC,EAAY,IAAI,IAGhBC,EAAyB,IAAI,IAG7BpC,EAAU,IAAIC,EAOpB,SAASoC,EAAYzC,EAAY0C,EAAwB,CACvDH,EAAU,IAAIvC,EAAI,CAAE,OAAA0C,EAAQ,MAAOJ,GAAQ,CAAC,EAC5CK,EAA4BD,EAAO,OAAQA,EAAO,KAAK,CACzD,CAGA,SAASE,EAAe5C,EAAY,CAClC,IAAM6C,EAAiBN,EAAU,IAAIvC,CAAE,GAAG,OAAO,OAGjD,GAFAuC,EAAU,OAAOvC,CAAE,EAEf6C,GAAkB,KAAM,OAI5B,IAAMC,EAAoB,CAAC,GAAGP,EAAU,OAAO,CAAC,EAC7C,OAAQQ,GAAMA,EAAE,OAAO,SAAWF,CAAc,EAChD,KAAK,CAACjB,EAAGC,IAAOD,EAAE,MAAQC,EAAE,MAAQ,GAAK,CAAE,EAE9C,GAAIiB,EAAkB,OAAS,EAAG,CAChC,IAAME,EAAeF,EAAkBA,EAAkB,OAAS,CAAC,EACnEH,EAA4BE,EAAgBG,EAAa,OAAO,KAAK,OAErEL,EAA4BE,EAAgB,MAAS,CAEzD,CAGA,SAASI,EAA4BtC,EAAkD,CACrF,IAAMuC,EAAgBlC,EAAkBrB,EAAWgB,CAAM,EACnDE,EAAeC,EAAgBH,CAAM,EACrCwC,EAAkBX,EAAuB,IAAI3B,CAAY,EAC/D,OAAQqC,GAAiBC,IAAoBA,IAAoB,KAC5D,CAAE,GAAGD,EAAe,GAAGC,CAAgB,EACxC,MACN,CAEA,IAAMC,EAA4FnC,IAAkB,CAClH,IAAIoC,EAAQC,EAAM,CAEhB,OAAIA,IAAS,MACH3C,GAAyB,CAC/B,IAAMuC,EAAgBG,EAAO,IAAI1C,CAAM,EACjCwC,EAAkBX,EAAuB,IAAI7B,CAAM,EACzD,OAAOwC,GAAmBA,EAAgBlC,CAAG,GAAK,KAAOkC,EAAgBlC,CAAG,EAAIiC,CAClF,EAIEI,IAAS,MACH3C,GACC0C,EAAO,IAAI1C,CAAM,GAAK6B,EAAuB,IAAI7B,CAAM,EAK9D2C,IAAS,OACJ,IAAM,IAAI,IAAI,CAAC,GAAGD,EAAO,KAAK,EAAG,GAAGb,EAAuB,KAAK,CAAC,CAAC,EAAE,OAAO,EAG7E,QAAQ,IAAIa,EAAQC,EAAMD,CAAM,CACzC,CACF,GAEME,EAAuD,CAAC,EAC9D,QAAWtC,KAAO,OAAO,KAAKtB,EAAU,MAAM,EAC5C4D,EAActC,CAAG,EAAI,IAAI,MAAMtB,EAAU,OAAOsB,CAAG,EAAGmC,EAAkBnC,CAAG,CAAC,EAE9E,IAAMuC,EAAcD,EAEdE,EAAsB,IAAI,MAAM9D,EAAW,CAC/C,IAAI0D,EAAQC,EAAM,CAChB,OAAIA,IAAS,cAAsBb,EAC/Ba,IAAS,iBAAyBV,EAClCU,IAAS,SAAiBE,EAC1BF,IAAS,UAAkBlD,EAC3BkD,IAAS,WACJ,IACL,IAAI,IAAI,CACN,GAAG/C,EAAkBiC,EAAuB,KAAK,EAAGhC,CAAe,EACnE,GAAG6C,EAAO,SAAS,CACrB,CAAC,EAAE,OAAO,EAEP,QAAQ,IAAIA,EAAQC,CAAI,CACjC,EACA,IAAID,EAAQC,EAAM,CAChB,OAAIA,IAAS,eAAiBA,IAAS,iBAAyB,GACzDA,KAAQD,CACjB,CACF,CAAC,EAGD,SAASV,EAA4BhC,EAAgBC,EAA8C,CACjG,IAAMC,EAAeC,EAAgBH,CAAM,EAErCI,EAAYkC,EAA4BtC,CAAM,EAChDC,IAAU,OAAW4B,EAAuB,IAAI3B,EAAcD,CAAK,EAClE4B,EAAuB,OAAO3B,CAAY,EAC/CT,EAAQ,KAAK,CAAE,OAAAO,EAAQ,MAAO,CAACsC,EAA4BtC,CAAM,EAAGI,CAAS,EAAG,UAAW0C,CAAoB,CAAC,CAClH,CAGA,OAAA9D,EAAU,QACP,KACC+D,EAAQC,GAAM,CAACnB,EAAuB,IAAI1B,EAAgB6C,EAAE,MAAM,CAAC,CAAC,EACpEC,EAAKlB,IAAY,CAAE,GAAGA,EAAQ,UAAWe,CAAoB,EAAE,CACjE,EACC,UAAUrD,CAAO,EAEbqD,CACT,CAEA,SAASI,EAAgBlE,EAAsBmE,EAAwC,CACrF,MAAO,cAAcA,KAAyBnE,EAAU,IAC1D,CAEO,SAASoE,GAAgBpE,EAAsBmE,EAAsC,CAC1F,aAAa,WAAWD,EAAgBlE,EAAWmE,CAAqB,CAAC,CAC3E,CAGO,SAASE,GACdrE,EACAmE,EACoB,CACpB,GAAM,CAAE,MAAAjE,EAAO,QAAAO,EAAS,OAAAF,CAAO,EAAIP,EAC7BsE,EAAUJ,EAAgBlE,EAAwBmE,CAAqB,EACzEI,EAAa,EACXC,EAAW,KAAK,IAAI,EAGpBC,EAAe,aAAa,QAAQH,CAAO,EACjD,GAAIG,EAAc,CAChB,IAAMC,EAAQ,KAAK,MAAMD,CAAY,EAC/BE,EAA0D,CAAC,EAEjE,OAAW,CAACrD,EAAKf,CAAM,IAAKmE,EAC1B,OAAW,CAAC1D,EAAQC,CAAK,IAAKV,EAC5BoE,EAAM3D,CAAM,EAAI2D,EAAM3D,CAAM,GAAK,CAAC,EAClC2D,EAAM3D,CAAM,EAAEM,CAAG,EAAIL,EAIzB,OAAW,CAAC2D,EAAU3D,CAAK,IAAK,OAAO,QAAQ0D,CAAK,EAAG,CACrD,IAAM3D,EAASd,EAAM,eAAe,CAAE,GAAI0E,CAAS,CAAC,EACpD7D,EAAaf,EAAWgB,EAAQC,CAA6B,EAG/D,QAAQ,KAAK,oBAAqBlB,EAAiBC,CAAS,EAAG,mBAAmB,EAMpF,IAAM6E,EAAYpE,EAAQ,UAAU,IAAM,CACxC8D,IACA,IAAMO,EAAU,KAAK,UACnB,OAAO,QAAQtE,EAAUD,EAASwE,GAAM,CAAC,GAAGA,EAAE,QAAQ,CAAC,EAAE,IAAKf,GAAM,CAACnD,EAAgBmD,EAAE,CAAC,CAAC,EAAGA,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CACrG,EACA,aAAa,QAAQM,EAASQ,CAAO,EACjCP,EAAa,KACf,QAAQ,KACN,YACAxE,EAAiBC,CAAS,EAC1B,qBACAuE,EACA,cACA,IAAI,KAAKC,CAAQ,EAAE,mBAAmB,EACtC,0GACF,CAEJ,CAAC,EACD,OAAAxE,EAAU,MAAM,iBAAiB,IAAM6E,GAAW,YAAY,CAAC,EAExD7E,CACT,CGlgBO,SAASgF,GACdC,EACAC,EACAC,EACQ,CACR,IAAMC,EAASH,EAAM,eAAeE,GAAW,CAAC,CAAC,EAEjD,GAAID,EACF,OAAW,CAACG,EAAWC,CAAK,IAAKJ,EAC/BK,EAAaF,EAAWD,EAAQE,CAAK,EAIzC,OAAOF,CACT,CAMO,SAASI,EAAgBC,EAAoC,CAClE,OAAO,OAAO,IAAIA,CAAY,CAChC,CAKO,SAASC,EAAgBN,EAA8B,CAC5D,OAAO,OAAO,OAAOA,CAAM,CAC7B","names":["Type","UpdateType","OptionalTypes","transformIterator","uuid","mapObject","filter","map","Subject","createIndexer","component","valueToEntities","getEntitiesWithValue","value","entities","getValueKey","getEntityString","add","entity","valueKey","entitiesWithValue","remove","getComponentEntities","getComponentValue","getEntitySymbol","subscription","map","pipe","isComponentUpdate","update","component","toUpdate","entity","value","getComponentValue","toUpdateStream","pipe","map","isIndexer","c","isFullComponentValue","key","getComponentName","component","defineComponent","world","schema","options","id","uuid","values","mapObject","update$","Subject","metadata","transformIterator","getEntityString","createIndexer","setComponent","entity","value","entitySymbol","getEntitySymbol","prevValue","getComponentValue","key","val","updateComponent","initialValue","currentValue","removeComponent","hasComponent","schemaKeys","OptionalTypes","getComponentValueStrict","componentValueEquals","a","b","equals","withValue","getEntitiesWithValue","isIndexer","isFullComponentValue","entities","getComponentEntities","overridableComponent","nonce","overrides","overriddenEntityValues","addOverride","update","setOverriddenComponentValue","removeOverride","affectedEntity","relevantOverrides","o","lastOverride","getOverriddenComponentValue","originalValue","overriddenValue","valueProxyHandler","target","prop","partialValues","valuesProxy","overriddenComponent","filter","e","map","getLocalCacheId","uniqueWorldIdentifier","clearLocalCache","createLocalCache","cacheId","numUpdates","creation","encodedCache","cache","state","entityId","updateSub","encoded","m","createEntity","world","components","options","entity","component","value","setComponent","getEntitySymbol","entityString","getEntityString"]}
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/Component.ts","../src/Indexer.ts","../src/utils.ts","../src/Entity.ts"],"sourcesContent":["/**\n * Type enum is used to specify value types in {@link ComponentSchema} to be able\n * to access type values in JavaScript in addition to TypeScript type checks.\n */\nexport enum Type {\n Boolean,\n Number,\n OptionalNumber,\n BigInt,\n OptionalBigInt,\n String,\n OptionalString,\n NumberArray,\n OptionalNumberArray,\n BigIntArray,\n OptionalBigIntArray,\n StringArray,\n OptionalStringArray,\n Entity,\n OptionalEntity,\n EntityArray,\n OptionalEntityArray,\n T,\n OptionalT,\n}\n\n/**\n * Used to specify type of {@link ComponentUpdate}.\n * - Enter: Update added a value to an entity that did not have a value before\n * - Exit: Update removed a value from an entity that had a value before\n * - Update: Update changed a value of an entity that already had a value before. Note: the value doesn't need to be different from the previous value.\n * - Noop: Update did nothing (removed a value from an entity that did not have a value)\n */\nexport enum UpdateType {\n Enter,\n Exit,\n Update,\n Noop,\n}\n\n/**\n * Helper constant with all optional {@link Type}s.\n */\nexport const OptionalTypes = [\n Type.OptionalEntity,\n Type.OptionalEntityArray,\n Type.OptionalNumber,\n Type.OptionalNumberArray,\n Type.OptionalBigInt,\n Type.OptionalBigIntArray,\n Type.OptionalString,\n Type.OptionalStringArray,\n Type.OptionalT,\n];\n","import { transformIterator, uuid } from \"@latticexyz/utils\";\nimport { mapObject } from \"@latticexyz/utils\";\nimport { filter, map, Subject } from \"rxjs\";\nimport { OptionalTypes } from \"./constants\";\nimport { createIndexer } from \"./Indexer\";\nimport {\n Component,\n ComponentValue,\n Entity,\n EntitySymbol,\n Indexer,\n Metadata,\n OverridableComponent,\n Override,\n Schema,\n World,\n} from \"./types\";\nimport { isFullComponentValue, isIndexer } from \"./utils\";\nimport { getEntityString, getEntitySymbol } from \"./Entity\";\n\nexport type ComponentMutationOptions = {\n /** Skip publishing this mutation to the component's update stream. Mostly used internally during initial hydration. */\n skipUpdateStream?: boolean;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction getComponentName(component: Component<any, any, any>) {\n return (\n component.metadata?.componentName ??\n component.metadata?.tableName ??\n component.metadata?.tableId ??\n component.metadata?.contractId ??\n component.id\n );\n}\n\n/**\n * Components contain state indexed by entities and are one of the fundamental building blocks in ECS.\n * Besides containing the state, components expose an rxjs update$ stream, that emits an event any time the value\n * of an entity in this component is updated.\n *\n * @param world {@link World} object this component should be registered onto.\n * @param schema {@link Schema} of component values. Uses Type enum as bridge between typescript types and javascript accessible values.\n * @param options Optional: {\n * id: descriptive id for this component (otherwise an autogenerated id is used),\n * metadata: arbitrary metadata,\n * indexed: if this flag is set, an indexer is applied to this component (see {@link createIndexer})\n * }\n * @returns Component object linked to the provided World\n *\n * @example\n * ```\n * const Position = defineComponent(world, { x: Type.Number, y: Type.Number }, { id: \"Position\" });\n * ```\n */\nexport function defineComponent<S extends Schema, M extends Metadata, T = unknown>(\n world: World,\n schema: S,\n options?: { id?: string; metadata?: M; indexed?: boolean },\n) {\n if (Object.keys(schema).length === 0) throw new Error(\"Component schema must have at least one key\");\n const id = options?.id ?? uuid();\n const values = mapObject(schema, () => new Map());\n const update$ = new Subject();\n const metadata = options?.metadata;\n const entities = () =>\n transformIterator((Object.values(values)[0] as Map<EntitySymbol, unknown>).keys(), getEntityString);\n let component = { values, schema, id, update$, metadata, entities, world } as Component<S, M, T>;\n if (options?.indexed) component = createIndexer(component);\n world.registerComponent(component as Component);\n return component;\n}\n\n/**\n * Set the value for a given entity in a given component.\n *\n * @param component {@link defineComponent Component} to be updated.\n * @param entity {@link Entity} whose value in the given component should be set.\n * @param value Value to set, schema must match the component schema.\n *\n * @example\n * ```\n * setComponent(Position, entity, { x: 1, y: 2 });\n * ```\n */\nexport function setComponent<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n entity: Entity,\n value: ComponentValue<S, T>,\n options: ComponentMutationOptions = {},\n) {\n const entitySymbol = getEntitySymbol(entity);\n const prevValue = getComponentValue(component, entity);\n for (const [key, val] of Object.entries(value)) {\n if (component.values[key]) {\n component.values[key].set(entitySymbol, val);\n } else {\n const isTableFieldIndex = component.metadata?.tableId && /^\\d+$/.test(key);\n if (!isTableFieldIndex) {\n // If this key looks like a field index from `defineStoreComponents`,\n // we can ignore this value without logging anything.\n //\n // Otherwise, we should let the user know we found undefined data.\n console.warn(\n \"Component definition for\",\n getComponentName(component),\n \"is missing key\",\n key,\n \", ignoring value\",\n val,\n \"for entity\",\n entity,\n \". Existing keys: \",\n Object.keys(component.values),\n );\n }\n }\n }\n if (!options.skipUpdateStream) {\n component.update$.next({ entity, value: [value, prevValue], component });\n }\n}\n\n/**\n * Update the value for a given entity in a given component while keeping the old value of keys not included in the update.\n *\n * @param component {@link defineComponent Component} to be updated.\n * @param entity {@link Entity} whose value in the given component should be updated.\n * @param value Partial value to be set, remaining keys will be taken from the existing component value.\n *\n * @remarks\n * This function fails silently during runtime if a partial value is set for an entity that\n * does not have a component value yet, since then a partial value will be set in the component for this entity.\n *\n * @example\n * ```\n * updateComponent(Position, entity, { x: 1 });\n * ```\n */\nexport function updateComponent<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n entity: Entity,\n value: Partial<ComponentValue<S, T>>,\n initialValue?: ComponentValue<S, T>,\n options: ComponentMutationOptions = {},\n) {\n const currentValue = getComponentValue(component, entity);\n if (currentValue === undefined) {\n if (initialValue === undefined) {\n throw new Error(`Can't update component ${getComponentName(component)} without a current value or initial value`);\n }\n setComponent(component, entity, { ...initialValue, ...value }, options);\n } else {\n setComponent(component, entity, { ...currentValue, ...value }, options);\n }\n}\n\n/**\n * Remove a given entity from a given component.\n *\n * @param component {@link defineComponent Component} to be updated.\n * @param entity {@link Entity} whose value should be removed from this component.\n */\nexport function removeComponent<S extends Schema, M extends Metadata, T = unknown>(\n component: Component<S, M, T>,\n entity: Entity,\n options: ComponentMutationOptions = {},\n) {\n const entitySymbol = getEntitySymbol(entity);\n const prevValue = getComponentValue(component, entity);\n for (const key of Object.keys(component.values)) {\n component.values[key].delete(entitySymbol);\n }\n if (!options.skipUpdateStream) {\n component.update$.next({ entity, value: [undefined, prevValue], component });\n }\n}\n\n/**\n * Check whether a component contains a value for a given entity.\n *\n * @param component {@link defineComponent Component} to check whether it has a value for the given entity.\n * @param entity {@link Entity} to check whether it has a value in the given component.\n * @returns true if the component contains a value for the given entity, else false.\n */\nexport function hasComponent<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n entity: Entity,\n): boolean {\n const entitySymbol = getEntitySymbol(entity);\n const map = Object.values(component.values)[0];\n return map.has(entitySymbol);\n}\n\n/**\n * Get the value of a given entity in the given component.\n * Returns undefined if no value or only a partial value is found.\n *\n * @param component {@link defineComponent Component} to get the value from for the given entity.\n * @param entity {@link Entity} to get the value for from the given component.\n * @returns Value of the given entity in the given component or undefined if no value exists.\n */\nexport function getComponentValue<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n entity: Entity,\n): ComponentValue<S, T> | undefined {\n const value: Record<string, unknown> = {};\n const entitySymbol = getEntitySymbol(entity);\n\n // Get the value of each schema key\n const schemaKeys = Object.keys(component.schema);\n for (const key of schemaKeys) {\n const val = component.values[key].get(entitySymbol);\n if (val === undefined && !OptionalTypes.includes(component.schema[key])) return undefined;\n value[key] = val;\n }\n\n return value as ComponentValue<S, T>;\n}\n\n/**\n * Get the value of a given entity in the given component.\n * Throws an error if no value exists for the given entity in the given component.\n *\n * @param component {@link defineComponent Component} to get the value from for the given entity.\n * @param entity {@link Entity} of the entity to get the value for from the given component.\n * @returns Value of the given entity in the given component.\n *\n * @remarks\n * Throws an error if no value exists in the component for the given entity.\n */\nexport function getComponentValueStrict<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n entity: Entity,\n): ComponentValue<S, T> {\n const value = getComponentValue(component, entity);\n if (!value) throw new Error(`No value for component ${getComponentName(component)} on entity ${entity}`);\n return value;\n}\n\n/**\n * Compare two {@link ComponentValue}s.\n * `a` can be a partial component value, in which case only the keys present in `a` are compared to the corresponding keys in `b`.\n *\n * @param a Partial {@link ComponentValue} to compare to `b`\n * @param b Component value to compare `a` to.\n * @returns True if `a` equals `b` in the keys present in a or neither `a` nor `b` are defined, else false.\n *\n * @example\n * ```\n * componentValueEquals({ x: 1, y: 2 }, { x: 1, y: 3 }) // returns false because value of y doesn't match\n * componentValueEquals({ x: 1 }, { x: 1, y: 3 }) // returns true because x is equal and y is not present in a\n * ```\n */\nexport function componentValueEquals<S extends Schema, T = unknown>(\n a?: Partial<ComponentValue<S, T>>,\n b?: ComponentValue<S, T>,\n): boolean {\n if (!a && !b) return true;\n if (!a || !b) return false;\n\n let equals = true;\n for (const key of Object.keys(a)) {\n equals = a[key] === b[key];\n if (!equals) return false;\n }\n return equals;\n}\n\n/**\n * Util to create a tuple of a component and value with matching schema.\n * (Used to enforce Typescript type safety.)\n *\n * @param component {@link defineComponent Component} with {@link ComponentSchema} `S`\n * @param value {@link ComponentValue} with {@link ComponentSchema} `S`\n * @returns Tuple `[component, value]`\n */\nexport function withValue<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n value: ComponentValue<S, T>,\n): [Component<S, Metadata, T>, ComponentValue<S, T>] {\n return [component, value];\n}\n\n/**\n * Get a set of entities that have the given component value in the given component.\n *\n * @param component {@link defineComponent Component} to get entities with the given value from.\n * @param value look for entities with this {@link ComponentValue}.\n * @returns Set with {@link Entity Entities} with the given component value.\n */\nexport function getEntitiesWithValue<S extends Schema>(\n component: Component<S> | Indexer<S>,\n value: Partial<ComponentValue<S>>,\n): Set<Entity> {\n // Shortcut for indexers\n if (isIndexer(component) && isFullComponentValue(component, value)) {\n return component.getEntitiesWithValue(value);\n }\n\n // Trivial implementation for regular components\n const entities = new Set<Entity>();\n for (const entity of getComponentEntities(component)) {\n const val = getComponentValue(component, entity);\n if (componentValueEquals(value, val)) {\n entities.add(entity);\n }\n }\n return entities;\n}\n\n/**\n * Get a set of all entities of the given component.\n *\n * @param component {@link defineComponent Component} to get all entities from\n * @returns Set of all entities in the given component.\n */\nexport function getComponentEntities<S extends Schema, T = unknown>(\n component: Component<S, Metadata, T>,\n): IterableIterator<Entity> {\n return component.entities();\n}\n\n/**\n * An overridable component is a mirror of the source component, with functions to lazily override specific entity values.\n * Lazily override means the values are not actually set to the source component, but the override is only returned if the value is read.\n *\n * - When an override for an entity is added to the component, the override is propagated via the component's `update$` stream.\n * - While an override is set for a specific entity, no updates to the source component for this entity will be propagated to the `update$` stream.\n * - When an override is removed for a specific entity and there are more overrides targeting this entity,\n * the override with the highest nonce will be propagated to the `update$` stream.\n * - When an override is removed for a specific entity and there are no more overrides targeting this entity,\n * the non-overridden underlying component value of this entity will be propagated to the `update$` stream.\n *\n * @param component {@link defineComponent Component} to use as underlying source for the overridable component\n * @returns overridable component\n */\nexport function overridableComponent<S extends Schema, M extends Metadata, T = unknown>(\n component: Component<S, M, T>,\n): OverridableComponent<S, M, T> {\n let nonce = 0;\n\n // Map from OverrideId to Override (to be able to add multiple overrides to the same Entity)\n const overrides = new Map<string, { update: Override<S, T>; nonce: number }>();\n\n // Map from EntitySymbol to current overridden component value\n const overriddenEntityValues = new Map<EntitySymbol, Partial<ComponentValue<S, T>> | null>();\n\n // Update event stream that takes into account overridden entity values\n const update$ = new Subject<{\n entity: Entity;\n value: [ComponentValue<S, T> | undefined, ComponentValue<S, T> | undefined];\n component: Component<S, Metadata, T>;\n }>();\n\n // Add a new override to some entity\n function addOverride(id: string, update: Override<S, T>) {\n overrides.set(id, { update, nonce: nonce++ });\n setOverriddenComponentValue(update.entity, update.value);\n }\n\n // Remove an override from an entity\n function removeOverride(id: string) {\n const affectedEntity = overrides.get(id)?.update.entity;\n overrides.delete(id);\n\n if (affectedEntity == null) return;\n\n // If there are more overries affecting this entity,\n // set the overriddenEntityValue to the last override\n const relevantOverrides = [...overrides.values()]\n .filter((o) => o.update.entity === affectedEntity)\n .sort((a, b) => (a.nonce < b.nonce ? -1 : 1));\n\n if (relevantOverrides.length > 0) {\n const lastOverride = relevantOverrides[relevantOverrides.length - 1];\n setOverriddenComponentValue(affectedEntity, lastOverride.update.value);\n } else {\n setOverriddenComponentValue(affectedEntity, undefined);\n }\n }\n\n // Internal function to get the current overridden value or value of the source component\n function getOverriddenComponentValue(entity: Entity): ComponentValue<S, T> | undefined {\n const originalValue = getComponentValue(component, entity);\n const entitySymbol = getEntitySymbol(entity);\n const overriddenValue = overriddenEntityValues.get(entitySymbol);\n return (originalValue || overriddenValue) && overriddenValue !== null // null is a valid override, in this case return undefined\n ? ({ ...originalValue, ...overriddenValue } as ComponentValue<S, T>)\n : undefined;\n }\n\n const valueProxyHandler: (key: keyof S) => ProxyHandler<(typeof component.values)[typeof key]> = (key: keyof S) => ({\n get(target, prop) {\n // Intercept calls to component.value[key].get(entity)\n if (prop === \"get\") {\n return (entity: EntitySymbol) => {\n const originalValue = target.get(entity);\n const overriddenValue = overriddenEntityValues.get(entity);\n return overriddenValue && overriddenValue[key] != null ? overriddenValue[key] : originalValue;\n };\n }\n\n // Intercept calls to component.value[key].has(entity)\n if (prop === \"has\") {\n return (entity: EntitySymbol) => {\n return target.has(entity) || overriddenEntityValues.has(entity);\n };\n }\n\n // Intercept calls to component.value[key].keys()\n if (prop === \"keys\") {\n return () => new Set([...target.keys(), ...overriddenEntityValues.keys()]).values();\n }\n\n return Reflect.get(target, prop, target);\n },\n });\n\n const partialValues: Partial<Component<S, M, T>[\"values\"]> = {};\n for (const key of Object.keys(component.values) as (keyof S)[]) {\n partialValues[key] = new Proxy(component.values[key], valueProxyHandler(key));\n }\n const valuesProxy = partialValues as Component<S, M, T>[\"values\"];\n\n const overriddenComponent = new Proxy(component, {\n get(target, prop) {\n if (prop === \"addOverride\") return addOverride;\n if (prop === \"removeOverride\") return removeOverride;\n if (prop === \"values\") return valuesProxy;\n if (prop === \"update$\") return update$;\n if (prop === \"entities\")\n return () =>\n new Set([\n ...transformIterator(overriddenEntityValues.keys(), getEntityString),\n ...target.entities(),\n ]).values();\n\n return Reflect.get(target, prop);\n },\n has(target, prop) {\n if (prop === \"addOverride\" || prop === \"removeOverride\") return true;\n return prop in target;\n },\n }) as OverridableComponent<S, M, T>;\n\n // Internal function to set the current overridden component value and emit the update event\n function setOverriddenComponentValue(entity: Entity, value?: Partial<ComponentValue<S, T>> | null) {\n const entitySymbol = getEntitySymbol(entity);\n // Check specifically for undefined - null is a valid override\n const prevValue = getOverriddenComponentValue(entity);\n if (value !== undefined) overriddenEntityValues.set(entitySymbol, value);\n else overriddenEntityValues.delete(entitySymbol);\n update$.next({ entity, value: [getOverriddenComponentValue(entity), prevValue], component: overriddenComponent });\n }\n\n // Channel through update events from the original component if there are no overrides\n component.update$\n .pipe(\n filter((e) => !overriddenEntityValues.get(getEntitySymbol(e.entity))),\n map((update) => ({ ...update, component: overriddenComponent })),\n )\n .subscribe(update$);\n\n return overriddenComponent;\n}\n\nfunction getLocalCacheId(component: Component, uniqueWorldIdentifier?: string): string {\n return `localcache-${uniqueWorldIdentifier}-${component.id}`;\n}\n\nexport function clearLocalCache(component: Component, uniqueWorldIdentifier?: string): void {\n localStorage.removeItem(getLocalCacheId(component, uniqueWorldIdentifier));\n}\n\n// Note: Only proof of concept for now - use this only for component that do not update frequently\nexport function createLocalCache<S extends Schema, M extends Metadata, T = unknown>(\n component: Component<S, M, T>,\n uniqueWorldIdentifier?: string,\n): Component<S, M, T> {\n const { world, update$, values } = component;\n const cacheId = getLocalCacheId(component as Component, uniqueWorldIdentifier);\n let numUpdates = 0;\n const creation = Date.now();\n\n // On creation, check if this component has locally cached values\n const encodedCache = localStorage.getItem(cacheId);\n if (encodedCache) {\n const cache = JSON.parse(encodedCache) as [string, [Entity, unknown][]][];\n const state: { [entity: Entity]: { [key: string]: unknown } } = {};\n\n for (const [key, values] of cache) {\n for (const [entity, value] of values) {\n state[entity] = state[entity] || {};\n state[entity][key] = value;\n }\n }\n\n for (const [entityId, value] of Object.entries(state)) {\n const entity = world.registerEntity({ id: entityId });\n setComponent(component, entity, value as ComponentValue<S, T>);\n }\n\n console.info(\"Loading component\", getComponentName(component), \"from local cache.\");\n }\n\n // Flush the entire component to the local cache every time it updates.\n // Note: this is highly unperformant and should only be used for components that\n // don't update often and don't have many values\n const updateSub = update$.subscribe(() => {\n numUpdates++;\n const encoded = JSON.stringify(\n Object.entries(mapObject(values, (m) => [...m.entries()].map((e) => [getEntityString(e[0]), e[1]]))),\n );\n localStorage.setItem(cacheId, encoded);\n if (numUpdates > 200) {\n console.warn(\n \"Component\",\n getComponentName(component),\n \"was locally cached\",\n numUpdates,\n \"times since\",\n new Date(creation).toLocaleTimeString(),\n \"- the local cache is in an alpha state and should not be used with components that update frequently yet\",\n );\n }\n });\n component.world.registerDisposer(() => updateSub?.unsubscribe());\n\n return component;\n}\n","import { getComponentEntities, getComponentValue } from \"./Component\";\nimport { getEntityString, getEntitySymbol } from \"./Entity\";\nimport { Component, ComponentValue, Entity, EntitySymbol, Indexer, Metadata, Schema } from \"./types\";\n\n/**\n * Create an indexed component from a given component.\n *\n * @remarks\n * An indexed component keeps a \"reverse mapping\" from {@link ComponentValue} to the Set of {@link createEntity Entities} with this value.\n * This adds a performance overhead to modifying component values and a memory overhead since in the worst case there is one\n * Set per entity (if every entity has a different component value).\n * In return the performance for querying for entities with a given component value is close to O(1) (instead of O(#entities) in a regular non-indexed component).\n * As a rule of thumb only components that are added to many entities and are queried with {@link HasValue} a lot should be indexed (eg. the Position component).\n *\n * @dev This could be made more (memory) efficient by using a hash of the component value as key, but would require handling hash collisions.\n *\n * @param component {@link defineComponent Component} to index.\n * @returns Indexed version of the component.\n */\nexport function createIndexer<S extends Schema, M extends Metadata, T = unknown>(\n component: Component<S, M, T>,\n): Indexer<S, M, T> {\n const valueToEntities = new Map<string, Set<EntitySymbol>>();\n\n function getEntitiesWithValue(value: ComponentValue<S, T>) {\n const entities = valueToEntities.get(getValueKey(value));\n return entities ? new Set([...entities].map(getEntityString)) : new Set<Entity>();\n }\n\n function getValueKey(value: ComponentValue<S, T>): string {\n return Object.values(value).join(\"/\");\n }\n\n function add(entity: EntitySymbol, value: ComponentValue<S, T> | undefined) {\n if (!value) return;\n const valueKey = getValueKey(value);\n let entitiesWithValue = valueToEntities.get(valueKey);\n if (!entitiesWithValue) {\n entitiesWithValue = new Set<EntitySymbol>();\n valueToEntities.set(valueKey, entitiesWithValue);\n }\n entitiesWithValue.add(entity);\n }\n\n function remove(entity: EntitySymbol, value: ComponentValue<S, T> | undefined) {\n if (!value) return;\n const valueKey = getValueKey(value);\n const entitiesWithValue = valueToEntities.get(valueKey);\n if (!entitiesWithValue) return;\n entitiesWithValue.delete(entity);\n }\n\n // Initial indexing\n for (const entity of getComponentEntities(component)) {\n const value = getComponentValue(component, entity);\n add(getEntitySymbol(entity), value);\n }\n\n // Keeping index up to date\n const subscription = component.update$.subscribe(({ entity, value }) => {\n // Remove from previous location\n remove(getEntitySymbol(entity), value[1]);\n\n // Add to new location\n add(getEntitySymbol(entity), value[0]);\n });\n\n component.world.registerDisposer(() => subscription?.unsubscribe());\n\n return { ...component, getEntitiesWithValue };\n}\n","import { map, pipe } from \"rxjs\";\nimport { getComponentValue } from \"./Component\";\nimport { UpdateType } from \"./constants\";\nimport { Component, ComponentUpdate, ComponentValue, Entity, Indexer, Schema } from \"./types\";\n\n/**\n * Type guard to infer the TypeScript type of a given component update\n *\n * @param update Component update to infer the type of.\n * @param component {@link defineComponent Component} to check whether the given update corresponds to it.\n * @returns True (+ infered type for `update`) if `update` belongs to `component`. Else false.\n */\nexport function isComponentUpdate<S extends Schema>(\n update: ComponentUpdate,\n component: Component<S>,\n): update is ComponentUpdate<S> {\n return update.component === component;\n}\n\n/**\n * Helper function to create a component update for the current component value of a given entity.\n *\n * @param entity Entity to create the component update for.\n * @param component Component to create the component update for.\n * @returns Component update corresponding to the given entity, the given component and the entity's current component value.\n */\nexport function toUpdate<S extends Schema>(entity: Entity, component: Component<S>) {\n const value = getComponentValue(component, entity);\n return {\n entity,\n component,\n value: [value, undefined],\n type: value == null ? UpdateType.Noop : UpdateType.Enter,\n } as ComponentUpdate<S> & {\n type: UpdateType;\n };\n}\n\n/**\n * Helper function to turn a stream of {@link Entity Entities} into a stream of component updates of the given component.\n * @param component Component to create update stream for.\n * @returns Unary function to be used with RxJS that turns stream of {@link Entity Entities} into stream of component updates.\n */\nexport function toUpdateStream<S extends Schema>(component: Component<S>) {\n return pipe(map((entity: Entity) => toUpdate(entity, component)));\n}\n\n/**\n * Helper function to check whether a given component is indexed.\n * @param c\n * @returns\n */\nexport function isIndexer<S extends Schema>(c: Component<S> | Indexer<S>): c is Indexer<S> {\n return \"getEntitiesWithValue\" in c;\n}\n\n/**\n * Helper function to check whether a given component value is partial or full.\n * @param component\n * @param value\n * @returns\n */\nexport function isFullComponentValue<S extends Schema>(\n component: Component<S>,\n value: Partial<ComponentValue<S>>,\n): value is ComponentValue<S> {\n return Object.keys(component.schema).every((key) => key in value);\n}\n","import { setComponent } from \"./Component\";\nimport { Component, ComponentValue, Entity, EntitySymbol, World } from \"./types\";\n\n/**\n * Register a new entity in the given {@link World} and initialize it with the given {@link ComponentValue}s.\n *\n * @param world World object this entity should be registered in.\n * @param components Array of [{@link defineComponent Component}, {@link ComponentValue}] tuples to be added to this entity.\n * (Use {@link withValue} to generate these tuples with type safety.)\n * @param options Optional: {\n * id: {@link Entity} for this entity. Use this for entities that were created outside of recs.\n * idSuffix: string to be appended to the auto-generated id. Use this for improved readability. Do not use this if the `id` option is provided.\n * }\n * @returns index of this entity in the {@link World}. This {@link Entity} is used to refer to this entity in other recs methods (eg {@link setComponent}).\n * (This is to avoid having to store strings in every component.)\n */\nexport function createEntity(\n world: World,\n components?: [Component, ComponentValue][],\n options?: { id?: string } | { idSuffix?: string },\n): Entity {\n const entity = world.registerEntity(options ?? {});\n\n if (components) {\n for (const [component, value] of components) {\n setComponent(component, entity, value);\n }\n }\n\n return entity;\n}\n\n/*\n * Get the symbol corresponding to an entity's string ID.\n * Entities are represented as symbols internally for memory efficiency.\n */\nexport function getEntitySymbol(entityString: string): EntitySymbol {\n return Symbol.for(entityString) as EntitySymbol;\n}\n\n/**\n * Get the underlying entity string of an entity symbol.\n */\nexport function getEntityString(entity: EntitySymbol): Entity {\n return Symbol.keyFor(entity) as Entity;\n}\n"],"mappings":"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","names":["Type","UpdateType","OptionalTypes","transformIterator","uuid","mapObject","filter","map","Subject","createIndexer","component","valueToEntities","getEntitiesWithValue","value","entities","getValueKey","getEntityString","add","entity","valueKey","entitiesWithValue","remove","getComponentEntities","getComponentValue","getEntitySymbol","subscription","map","pipe","isComponentUpdate","update","component","toUpdate","entity","value","getComponentValue","toUpdateStream","pipe","map","isIndexer","c","isFullComponentValue","key","getComponentName","component","defineComponent","world","schema","options","id","uuid","values","mapObject","update$","Subject","metadata","transformIterator","getEntityString","createIndexer","setComponent","entity","value","entitySymbol","getEntitySymbol","prevValue","getComponentValue","key","val","updateComponent","initialValue","currentValue","removeComponent","hasComponent","schemaKeys","OptionalTypes","getComponentValueStrict","componentValueEquals","a","b","equals","withValue","getEntitiesWithValue","isIndexer","isFullComponentValue","entities","getComponentEntities","overridableComponent","nonce","overrides","overriddenEntityValues","addOverride","update","setOverriddenComponentValue","removeOverride","affectedEntity","relevantOverrides","o","lastOverride","getOverriddenComponentValue","originalValue","overriddenValue","valueProxyHandler","target","prop","partialValues","valuesProxy","overriddenComponent","filter","e","map","getLocalCacheId","uniqueWorldIdentifier","clearLocalCache","createLocalCache","cacheId","numUpdates","creation","encodedCache","cache","state","entityId","updateSub","encoded","m","createEntity","world","components","options","entity","component","value","setComponent","getEntitySymbol","entityString","getEntityString"]}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { g as Components, E as Entity, o as Override, n as SchemaOf, W as World, S as Schema, M as Metadata, C as Component, O as OverridableComponent, T as Type } from '../types-
|
1
|
+
import { g as Components, E as Entity, o as Override, n as SchemaOf, W as World, S as Schema, M as Metadata, C as Component, O as OverridableComponent, T as Type } from '../types-CAQycjNT.js';
|
2
2
|
import { Observable } from 'rxjs';
|
3
3
|
import { ValueOf } from '@latticexyz/utils';
|
4
4
|
import 'type-fest';
|
@@ -48,4 +48,4 @@ declare function createActionSystem<M = unknown>(world: World, txReduced$: Obser
|
|
48
48
|
}, Metadata, M>;
|
49
49
|
};
|
50
50
|
|
51
|
-
export { ActionState, ActionSystem, createActionSystem };
|
51
|
+
export { ActionState, type ActionSystem, createActionSystem };
|
package/dist/deprecated/index.js
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
import{d as h,m as O,n as T,o as a,r as c,x as v}from"../chunk-
|
1
|
+
import{d as h,m as O,n as T,o as a,r as c,x as v}from"../chunk-2YR7WENB.js";var b=(s=>(s.Requested="Requested",s.Executing="Executing",s.WaitingForTxEvents="WaitingForTxEvents",s.Complete="Complete",s.Failed="Failed",s.Cancelled="Cancelled",s.TxReduced="TxReduced",s))(b||{});import{merge as S}from"rxjs";import{mapObject as D,awaitStreamValue as F,uuid as R}from"@latticexyz/utils";function A(d){return O(d,{state:5,on:14,metadata:18,overrides:12,txHash:6},{id:"Action"})}function L(d,x,C){let o=A(d),m={},u=new Map,f=new Map;d.registerDisposer(()=>{for(let{dispose:t}of f.values())t()});function s(t){let n=m[t.id]||v(t);return m[t.id]||(m[t.id]=n),n}function E(t){if(d.hasEntity(t.id))return console.warn(`Action with id ${t.id} is already requested.`),t.id;let i=h(d,void 0,{id:t.id});T(o,i,{state:"Requested",on:t.on,metadata:t.metadata,overrides:void 0,txHash:void 0});for(let r of Object.values(t.components))m[r.id]||(m[r.id]=v(r));let e={...t,entity:i,componentsWithOptimisticUpdates:D(t.components,r=>s(r))};u.set(e.id,e);let p=S(...Object.values(e.componentsWithOptimisticUpdates).map(r=>r.update$)).subscribe(()=>g(e));return g(e),f.set(e.id,{dispose:()=>p?.unsubscribe()}),i}function g(t){if(c(o,t.entity)?.state!=="Requested")return;let n=t.requirement(t.componentsWithOptimisticUpdates);n&&w(t,n)}async function w(t,n){if(c(o,t.entity)?.state!=="Requested")return;a(o,t.entity,{state:"Executing"});let i=t.updates(t.componentsWithOptimisticUpdates,n).map(e=>({...e,id:R()}));a(o,t.entity,{overrides:i.map(e=>`${e.id}/${e.component.id}`)});for(let{component:e,value:p,entity:r,id:y}of i)m[e.id].addOverride(y,{entity:r,value:p});try{let e=await t.execute(n);if(e&&(a(o,t.entity,{state:"WaitingForTxEvents",txHash:e}),await F(x,p=>p===e),a(o,t.entity,{state:"TxReduced"}),t.awaitConfirmation))if(C)await C(e);else throw new Error("action has awaitConfirmation but no waitForTransaction specified in createActionSystem");a(o,t.entity,{state:"Complete"})}catch(e){M(e,t)}l(t.id)}function M(t,n){console.error(t),a(o,n.entity,{state:"Failed"}),l(n.id)}function W(t){let n=u.get(t);return!n||c(o,n.entity)?.state!=="Requested"?(console.warn(`Action ${t} was not found or is not in the "Requested" state.`),!1):(a(o,n.entity,{state:"Cancelled"}),l(t),!0)}function l(t){if(!u.get(t)){console.warn(`Trying to remove action ${t} that does not exist.`);return}let i=t,e=i!=null&&c(o,i)?.overrides||[];for(let p of e){let[r,y]=p.split("/");m[y].removeOverride(r)}f.get(t)?.dispose(),f.delete(t),u.delete(t),i!=null&&setTimeout(()=>d.deleteEntity(i),5e3)}return{add:E,cancel:W,withOptimisticUpdates:s,Action:o}}export{b as ActionState,L as createActionSystem};
|
2
2
|
//# sourceMappingURL=index.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../src/deprecated/constants.ts","../../src/deprecated/createActionSystem.ts","../../src/deprecated/defineActionComponent.ts"],"sourcesContent":["export enum ActionState {\n Requested = \"Requested\",\n Executing = \"Executing\",\n WaitingForTxEvents = \"WaitingForTxEvents\",\n Complete = \"Complete\",\n Failed = \"Failed\",\n Cancelled = \"Cancelled\",\n TxReduced = \"TxReduced\",\n}\n","import { merge, Observable } from \"rxjs\";\nimport { mapObject, awaitStreamValue, uuid } from \"@latticexyz/utils\";\nimport { ActionState } from \"./constants\";\nimport { ActionData, ActionRequest } from \"./types\";\nimport { defineActionComponent } from \"./defineActionComponent\";\nimport { overridableComponent, setComponent, getComponentValue, updateComponent } from \"../Component\";\nimport { createEntity } from \"../Entity\";\nimport { World, OverridableComponent, Metadata, Component, Components, Entity, Schema } from \"../types\";\n\nexport type ActionSystem = ReturnType<typeof createActionSystem>;\n\n/**\n * @deprecated For now, we suggest using `overridableComponent(Component)` and `addOverride`/`removeOverride` to manage overrides yourself.\n */\nexport function createActionSystem<M = unknown>(\n world: World,\n txReduced$: Observable<string>,\n waitForTransaction?: (tx: string) => Promise<void>,\n) {\n // Action component\n const Action = defineActionComponent<M>(world);\n\n // Components that scheduled actions depend on including pending updates\n const componentsWithOptimisticUpdates: { [id: string]: OverridableComponent<Schema> } = {};\n\n // ActionData contains requirements and execute logic of scheduled actions.\n // We also store the relevant subset of all componentsWithOptimisticUpdates in the action data,\n // to recheck requirements only if relevant components updated.\n const actionData = new Map<string, ActionData>();\n\n // Disposers of requirement check autoruns for all pending actions\n const disposer = new Map<string, { dispose: () => void }>();\n world.registerDisposer(() => {\n for (const { dispose } of disposer.values()) dispose();\n });\n\n /**\n * Maps all components in a given components map to the respective components including pending updates\n * @param component Component to be mapped to components including pending updates\n * @returns Components including pending updates\n */\n function withOptimisticUpdates<S extends Schema, M extends Metadata, T>(\n component: Component<S, M, T>,\n ): OverridableComponent<S, M, T> {\n const optimisticComponent = componentsWithOptimisticUpdates[component.id] || overridableComponent(component);\n\n // If the component is not tracked yet, add it to the map of overridable components\n if (!componentsWithOptimisticUpdates[component.id]) {\n componentsWithOptimisticUpdates[component.id] = optimisticComponent;\n }\n\n // Typescript can't know that the optimistic component with this id has the same type as C\n return optimisticComponent as OverridableComponent<S, M, T>;\n }\n\n /**\n * Schedules an action. The action will be executed once its requirement is fulfilled.\n * Note: the requirement will only be rechecked automatically if the requirement is based on components\n * (or other mobx-observable values).\n * @param actionRequest Action to be scheduled\n * @returns index of the entity created for the action\n */\n function add<C extends Components, T>(actionRequest: ActionRequest<C, T, M>): Entity {\n // Prevent the same actions from being scheduled multiple times\n const existingAction = world.hasEntity(actionRequest.id as Entity);\n if (existingAction) {\n console.warn(`Action with id ${actionRequest.id} is already requested.`);\n return actionRequest.id as Entity;\n }\n\n // Set the action component\n const entity = createEntity(world, undefined, {\n id: actionRequest.id,\n });\n\n setComponent(Action, entity, {\n state: ActionState.Requested,\n on: actionRequest.on,\n metadata: actionRequest.metadata,\n overrides: undefined,\n txHash: undefined,\n });\n\n // Add components that are not tracked yet to internal overridable component map.\n // Pending updates will be applied to internal overridable components.\n for (const component of Object.values(actionRequest.components)) {\n if (!componentsWithOptimisticUpdates[component.id])\n componentsWithOptimisticUpdates[component.id] = overridableComponent(component);\n }\n\n // Store relevant components with pending updates along the action's requirement and execution logic\n const action = {\n ...actionRequest,\n entity,\n componentsWithOptimisticUpdates: mapObject(actionRequest.components, (c) => withOptimisticUpdates(c)),\n } as unknown as ActionData;\n actionData.set(action.id, action);\n\n // This subscriotion makes sure the action requirement is checked again every time\n // one of the referenced components changes or the pending updates map changes\n const subscription = merge(\n ...Object.values(action.componentsWithOptimisticUpdates).map((c) => c.update$),\n ).subscribe(() => checkRequirement(action));\n checkRequirement(action);\n disposer.set(action.id, { dispose: () => subscription?.unsubscribe() });\n\n return entity;\n }\n\n /**\n * Checks the requirement of a given action and executes the action if the requirement is fulfilled\n * @param action Action to check the requirement of\n * @returns void\n */\n function checkRequirement(action: ActionData) {\n // Only check requirements of requested actions\n if (getComponentValue(Action, action.entity)?.state !== ActionState.Requested) return;\n\n // Check requirement on components including pending updates\n const requirementResult = action.requirement(action.componentsWithOptimisticUpdates);\n\n // Execute the action if the requirements are met\n if (requirementResult) executeAction(action, requirementResult);\n }\n\n /**\n * Executes the given action and sets the corresponding Action component\n * @param action ActionData of the action to be executed\n * @param requirementResult Result of the action's requirement function\n * @returns void\n */\n async function executeAction<T>(action: ActionData, requirementResult: T) {\n // Only execute actions that were requested before\n if (getComponentValue(Action, action.entity)?.state !== ActionState.Requested) return;\n\n // Update the action state\n updateComponent(Action, action.entity, { state: ActionState.Executing });\n\n // Compute overrides\n const overrides = action\n .updates(action.componentsWithOptimisticUpdates, requirementResult)\n .map((o) => ({ ...o, id: uuid() }));\n\n // Store overrides on Action component to be able to remove when action is done\n updateComponent(Action, action.entity, { overrides: overrides.map((o) => `${o.id}/${o.component.id}`) });\n\n // Set all pending updates of this action\n for (const { component, value, entity, id } of overrides) {\n componentsWithOptimisticUpdates[component.id].addOverride(id, { entity, value });\n }\n\n try {\n // Execute the action\n const tx = await action.execute(requirementResult);\n\n // If the result includes a hash key (single tx) or hashes (multiple tx) key, wait for the transactions to complete before removing the pending actions\n if (tx) {\n // Wait for all tx events to be reduced\n updateComponent(Action, action.entity, { state: ActionState.WaitingForTxEvents, txHash: tx });\n await awaitStreamValue(txReduced$, (v) => v === tx);\n updateComponent(Action, action.entity, { state: ActionState.TxReduced });\n // TODO: extend ActionData type to be aware of whether waitForTransaction is set\n if (action.awaitConfirmation) {\n if (waitForTransaction) {\n await waitForTransaction(tx);\n } else {\n throw new Error(\"action has awaitConfirmation but no waitForTransaction specified in createActionSystem\");\n }\n }\n }\n\n updateComponent(Action, action.entity, { state: ActionState.Complete });\n } catch (e) {\n handleError(e, action);\n }\n\n // After the action is done executing (failed or completed), remove its actionData and remove the Action component\n remove(action.id);\n }\n\n // Set the action's state to ActionState.Failed\n function handleError(e: unknown, action: ActionData) {\n console.error(e);\n updateComponent(Action, action.entity, { state: ActionState.Failed });\n remove(action.id);\n }\n\n /**\n * Cancels the action with the given ID if it is in the \"Requested\" state.\n * @param actionId ID of the action to be cancelled\n * @returns void\n */\n function cancel(actionId: string): boolean {\n const action = actionData.get(actionId);\n if (!action || getComponentValue(Action, action.entity)?.state !== ActionState.Requested) {\n console.warn(`Action ${actionId} was not found or is not in the \"Requested\" state.`);\n return false;\n }\n updateComponent(Action, action.entity, { state: ActionState.Cancelled });\n remove(actionId);\n return true;\n }\n\n /**\n * Removes actionData disposer of the action with the given ID and removes its pending updates.\n * @param actionId ID of the action to be removed\n */\n function remove(actionId: string) {\n const action = actionData.get(actionId);\n if (!action) {\n console.warn(`Trying to remove action ${actionId} that does not exist.`);\n return;\n }\n\n // Remove this action's pending updates\n const actionEntity = actionId as Entity;\n const overrides = (actionEntity != null && getComponentValue(Action, actionEntity)?.overrides) || [];\n for (const override of overrides) {\n const [id, componentId] = override.split(\"/\");\n const component = componentsWithOptimisticUpdates[componentId];\n component.removeOverride(id);\n }\n\n // Remove this action's autorun and corresponding disposer\n disposer.get(actionId)?.dispose();\n disposer.delete(actionId);\n\n // Remove the action data\n actionData.delete(actionId);\n\n // Remove the action entity after some time\n actionEntity != null && setTimeout(() => world.deleteEntity(actionEntity), 5000);\n }\n\n return { add, cancel, withOptimisticUpdates, Action };\n}\n","import { defineComponent } from \"../Component\";\nimport { Type } from \"../constants\";\nimport { World, Component, SchemaOf, Metadata } from \"../types\";\n\nexport function defineActionComponent<T = unknown>(world: World) {\n const Action = defineComponent(\n world,\n {\n state: Type.String,\n on: Type.OptionalEntity,\n metadata: Type.OptionalT,\n overrides: Type.OptionalStringArray,\n txHash: Type.OptionalString,\n },\n { id: \"Action\" },\n );\n return Action as Component<SchemaOf<typeof Action>, Metadata, T>;\n}\n"],"mappings":"4EAAO,IAAKA,OACVA,EAAA,UAAY,YACZA,EAAA,UAAY,YACZA,EAAA,mBAAqB,qBACrBA,EAAA,SAAW,WACXA,EAAA,OAAS,SACTA,EAAA,UAAY,YACZA,EAAA,UAAY,YAPFA,OAAA,ICAZ,OAAS,SAAAC,MAAyB,OAClC,OAAS,aAAAC,EAAW,oBAAAC,EAAkB,QAAAC,MAAY,oBCG3C,SAASC,EAAmCC,EAAc,CAY/D,OAXeC,EACbD,EACA,CACE,QACA,MACA,YACA,aACA,QACF,EACA,CAAE,GAAI,QAAS,CACjB,CAEF,CDHO,SAASE,EACdC,EACAC,EACAC,EACA,CAEA,IAAMC,EAASC,EAAyBJ,CAAK,EAGvCK,EAAkF,CAAC,EAKnFC,EAAa,IAAI,IAGjBC,EAAW,IAAI,IACrBP,EAAM,iBAAiB,IAAM,CAC3B,OAAW,CAAE,QAAAQ,CAAQ,IAAKD,EAAS,OAAO,EAAGC,EAAQ,CACvD,CAAC,EAOD,SAASC,EACPC,EAC+B,CAC/B,IAAMC,EAAsBN,EAAgCK,EAAU,EAAE,GAAKE,EAAqBF,CAAS,EAG3G,OAAKL,EAAgCK,EAAU,EAAE,IAC/CL,EAAgCK,EAAU,EAAE,EAAIC,GAI3CA,CACT,CASA,SAASE,EAA6BC,EAA+C,CAGnF,GADuBd,EAAM,UAAUc,EAAc,EAAY,EAE/D,eAAQ,KAAK,kBAAkBA,EAAc,0BAA0B,EAChEA,EAAc,GAIvB,IAAMC,EAASC,EAAahB,EAAO,OAAW,CAC5C,GAAIc,EAAc,EACpB,CAAC,EAEDG,EAAad,EAAQY,EAAQ,CAC3B,kBACA,GAAID,EAAc,GAClB,SAAUA,EAAc,SACxB,UAAW,OACX,OAAQ,MACV,CAAC,EAID,QAAWJ,KAAa,OAAO,OAAOI,EAAc,UAAU,EACvDT,EAAgCK,EAAU,EAAE,IAC/CL,EAAgCK,EAAU,EAAE,EAAIE,EAAqBF,CAAS,GAIlF,IAAMQ,EAAS,CACb,GAAGJ,EACH,OAAAC,EACA,gCAAiCI,EAAUL,EAAc,WAAaM,GAAMX,EAAsBW,CAAC,CAAC,CACtG,EACAd,EAAW,IAAIY,EAAO,GAAIA,CAAM,EAIhC,IAAMG,EAAeC,EACnB,GAAG,OAAO,OAAOJ,EAAO,+BAA+B,EAAE,IAAKE,GAAMA,EAAE,OAAO,CAC/E,EAAE,UAAU,IAAMG,EAAiBL,CAAM,CAAC,EAC1C,OAAAK,EAAiBL,CAAM,EACvBX,EAAS,IAAIW,EAAO,GAAI,CAAE,QAAS,IAAMG,GAAc,YAAY,CAAE,CAAC,EAE/DN,CACT,CAOA,SAASQ,EAAiBL,EAAoB,CAE5C,GAAIM,EAAkBrB,EAAQe,EAAO,MAAM,GAAG,oBAAiC,OAG/E,IAAMO,EAAoBP,EAAO,YAAYA,EAAO,+BAA+B,EAG/EO,GAAmBC,EAAcR,EAAQO,CAAiB,CAChE,CAQA,eAAeC,EAAiBR,EAAoBO,EAAsB,CAExE,GAAID,EAAkBrB,EAAQe,EAAO,MAAM,GAAG,oBAAiC,OAG/ES,EAAgBxB,EAAQe,EAAO,OAAQ,CAAE,iBAA6B,CAAC,EAGvE,IAAMU,EAAYV,EACf,QAAQA,EAAO,gCAAiCO,CAAiB,EACjE,IAAKI,IAAO,CAAE,GAAGA,EAAG,GAAIC,EAAK,CAAE,EAAE,EAGpCH,EAAgBxB,EAAQe,EAAO,OAAQ,CAAE,UAAWU,EAAU,IAAKC,GAAM,GAAGA,EAAE,MAAMA,EAAE,UAAU,IAAI,CAAE,CAAC,EAGvG,OAAW,CAAE,UAAAnB,EAAW,MAAAqB,EAAO,OAAAhB,EAAQ,GAAAiB,CAAG,IAAKJ,EAC7CvB,EAAgCK,EAAU,EAAE,EAAE,YAAYsB,EAAI,CAAE,OAAAjB,EAAQ,MAAAgB,CAAM,CAAC,EAGjF,GAAI,CAEF,IAAME,EAAK,MAAMf,EAAO,QAAQO,CAAiB,EAGjD,GAAIQ,IAEFN,EAAgBxB,EAAQe,EAAO,OAAQ,CAAE,2BAAuC,OAAQe,CAAG,CAAC,EAC5F,MAAMC,EAAiBjC,EAAakC,GAAMA,IAAMF,CAAE,EAClDN,EAAgBxB,EAAQe,EAAO,OAAQ,CAAE,iBAA6B,CAAC,EAEnEA,EAAO,mBACT,GAAIhB,EACF,MAAMA,EAAmB+B,CAAE,MAE3B,OAAM,IAAI,MAAM,wFAAwF,EAK9GN,EAAgBxB,EAAQe,EAAO,OAAQ,CAAE,gBAA4B,CAAC,CACxE,OAAS,EAAP,CACAkB,EAAY,EAAGlB,CAAM,CACvB,CAGAmB,EAAOnB,EAAO,EAAE,CAClB,CAGA,SAASkB,EAAYE,EAAYpB,EAAoB,CACnD,QAAQ,MAAMoB,CAAC,EACfX,EAAgBxB,EAAQe,EAAO,OAAQ,CAAE,cAA0B,CAAC,EACpEmB,EAAOnB,EAAO,EAAE,CAClB,CAOA,SAASqB,EAAOC,EAA2B,CACzC,IAAMtB,EAASZ,EAAW,IAAIkC,CAAQ,EACtC,MAAI,CAACtB,GAAUM,EAAkBrB,EAAQe,EAAO,MAAM,GAAG,qBACvD,QAAQ,KAAK,UAAUsB,qDAA4D,EAC5E,KAETb,EAAgBxB,EAAQe,EAAO,OAAQ,CAAE,iBAA6B,CAAC,EACvEmB,EAAOG,CAAQ,EACR,GACT,CAMA,SAASH,EAAOG,EAAkB,CAEhC,GAAI,CADWlC,EAAW,IAAIkC,CAAQ,EACzB,CACX,QAAQ,KAAK,2BAA2BA,wBAA+B,EACvE,OAIF,IAAMC,EAAeD,EACfZ,EAAaa,GAAgB,MAAQjB,EAAkBrB,EAAQsC,CAAY,GAAG,WAAc,CAAC,EACnG,QAAWC,KAAYd,EAAW,CAChC,GAAM,CAACI,EAAIW,CAAW,EAAID,EAAS,MAAM,GAAG,EAC1BrC,EAAgCsC,CAAW,EACnD,eAAeX,CAAE,EAI7BzB,EAAS,IAAIiC,CAAQ,GAAG,QAAQ,EAChCjC,EAAS,OAAOiC,CAAQ,EAGxBlC,EAAW,OAAOkC,CAAQ,EAG1BC,GAAgB,MAAQ,WAAW,IAAMzC,EAAM,aAAayC,CAAY,EAAG,GAAI,CACjF,CAEA,MAAO,CAAE,IAAA5B,EAAK,OAAA0B,EAAQ,sBAAA9B,EAAuB,OAAAN,CAAO,CACtD","names":["ActionState","merge","mapObject","awaitStreamValue","uuid","defineActionComponent","world","defineComponent","createActionSystem","world","txReduced$","waitForTransaction","Action","defineActionComponent","componentsWithOptimisticUpdates","actionData","disposer","dispose","withOptimisticUpdates","component","optimisticComponent","overridableComponent","add","actionRequest","entity","createEntity","setComponent","action","mapObject","c","subscription","merge","checkRequirement","getComponentValue","requirementResult","executeAction","updateComponent","overrides","o","uuid","value","id","tx","awaitStreamValue","v","handleError","remove","e","cancel","actionId","actionEntity","override","componentId"]}
|
1
|
+
{"version":3,"sources":["../../src/deprecated/constants.ts","../../src/deprecated/createActionSystem.ts","../../src/deprecated/defineActionComponent.ts"],"sourcesContent":["export enum ActionState {\n Requested = \"Requested\",\n Executing = \"Executing\",\n WaitingForTxEvents = \"WaitingForTxEvents\",\n Complete = \"Complete\",\n Failed = \"Failed\",\n Cancelled = \"Cancelled\",\n TxReduced = \"TxReduced\",\n}\n","import { merge, Observable } from \"rxjs\";\nimport { mapObject, awaitStreamValue, uuid } from \"@latticexyz/utils\";\nimport { ActionState } from \"./constants\";\nimport { ActionData, ActionRequest } from \"./types\";\nimport { defineActionComponent } from \"./defineActionComponent\";\nimport { overridableComponent, setComponent, getComponentValue, updateComponent } from \"../Component\";\nimport { createEntity } from \"../Entity\";\nimport { World, OverridableComponent, Metadata, Component, Components, Entity, Schema } from \"../types\";\n\nexport type ActionSystem = ReturnType<typeof createActionSystem>;\n\n/**\n * @deprecated For now, we suggest using `overridableComponent(Component)` and `addOverride`/`removeOverride` to manage overrides yourself.\n */\nexport function createActionSystem<M = unknown>(\n world: World,\n txReduced$: Observable<string>,\n waitForTransaction?: (tx: string) => Promise<void>,\n) {\n // Action component\n const Action = defineActionComponent<M>(world);\n\n // Components that scheduled actions depend on including pending updates\n const componentsWithOptimisticUpdates: { [id: string]: OverridableComponent<Schema> } = {};\n\n // ActionData contains requirements and execute logic of scheduled actions.\n // We also store the relevant subset of all componentsWithOptimisticUpdates in the action data,\n // to recheck requirements only if relevant components updated.\n const actionData = new Map<string, ActionData>();\n\n // Disposers of requirement check autoruns for all pending actions\n const disposer = new Map<string, { dispose: () => void }>();\n world.registerDisposer(() => {\n for (const { dispose } of disposer.values()) dispose();\n });\n\n /**\n * Maps all components in a given components map to the respective components including pending updates\n * @param component Component to be mapped to components including pending updates\n * @returns Components including pending updates\n */\n function withOptimisticUpdates<S extends Schema, M extends Metadata, T>(\n component: Component<S, M, T>,\n ): OverridableComponent<S, M, T> {\n const optimisticComponent = componentsWithOptimisticUpdates[component.id] || overridableComponent(component);\n\n // If the component is not tracked yet, add it to the map of overridable components\n if (!componentsWithOptimisticUpdates[component.id]) {\n componentsWithOptimisticUpdates[component.id] = optimisticComponent;\n }\n\n // Typescript can't know that the optimistic component with this id has the same type as C\n return optimisticComponent as OverridableComponent<S, M, T>;\n }\n\n /**\n * Schedules an action. The action will be executed once its requirement is fulfilled.\n * Note: the requirement will only be rechecked automatically if the requirement is based on components\n * (or other mobx-observable values).\n * @param actionRequest Action to be scheduled\n * @returns index of the entity created for the action\n */\n function add<C extends Components, T>(actionRequest: ActionRequest<C, T, M>): Entity {\n // Prevent the same actions from being scheduled multiple times\n const existingAction = world.hasEntity(actionRequest.id as Entity);\n if (existingAction) {\n console.warn(`Action with id ${actionRequest.id} is already requested.`);\n return actionRequest.id as Entity;\n }\n\n // Set the action component\n const entity = createEntity(world, undefined, {\n id: actionRequest.id,\n });\n\n setComponent(Action, entity, {\n state: ActionState.Requested,\n on: actionRequest.on,\n metadata: actionRequest.metadata,\n overrides: undefined,\n txHash: undefined,\n });\n\n // Add components that are not tracked yet to internal overridable component map.\n // Pending updates will be applied to internal overridable components.\n for (const component of Object.values(actionRequest.components)) {\n if (!componentsWithOptimisticUpdates[component.id])\n componentsWithOptimisticUpdates[component.id] = overridableComponent(component);\n }\n\n // Store relevant components with pending updates along the action's requirement and execution logic\n const action = {\n ...actionRequest,\n entity,\n componentsWithOptimisticUpdates: mapObject(actionRequest.components, (c) => withOptimisticUpdates(c)),\n } as unknown as ActionData;\n actionData.set(action.id, action);\n\n // This subscriotion makes sure the action requirement is checked again every time\n // one of the referenced components changes or the pending updates map changes\n const subscription = merge(\n ...Object.values(action.componentsWithOptimisticUpdates).map((c) => c.update$),\n ).subscribe(() => checkRequirement(action));\n checkRequirement(action);\n disposer.set(action.id, { dispose: () => subscription?.unsubscribe() });\n\n return entity;\n }\n\n /**\n * Checks the requirement of a given action and executes the action if the requirement is fulfilled\n * @param action Action to check the requirement of\n * @returns void\n */\n function checkRequirement(action: ActionData) {\n // Only check requirements of requested actions\n if (getComponentValue(Action, action.entity)?.state !== ActionState.Requested) return;\n\n // Check requirement on components including pending updates\n const requirementResult = action.requirement(action.componentsWithOptimisticUpdates);\n\n // Execute the action if the requirements are met\n if (requirementResult) executeAction(action, requirementResult);\n }\n\n /**\n * Executes the given action and sets the corresponding Action component\n * @param action ActionData of the action to be executed\n * @param requirementResult Result of the action's requirement function\n * @returns void\n */\n async function executeAction<T>(action: ActionData, requirementResult: T) {\n // Only execute actions that were requested before\n if (getComponentValue(Action, action.entity)?.state !== ActionState.Requested) return;\n\n // Update the action state\n updateComponent(Action, action.entity, { state: ActionState.Executing });\n\n // Compute overrides\n const overrides = action\n .updates(action.componentsWithOptimisticUpdates, requirementResult)\n .map((o) => ({ ...o, id: uuid() }));\n\n // Store overrides on Action component to be able to remove when action is done\n updateComponent(Action, action.entity, { overrides: overrides.map((o) => `${o.id}/${o.component.id}`) });\n\n // Set all pending updates of this action\n for (const { component, value, entity, id } of overrides) {\n componentsWithOptimisticUpdates[component.id].addOverride(id, { entity, value });\n }\n\n try {\n // Execute the action\n const tx = await action.execute(requirementResult);\n\n // If the result includes a hash key (single tx) or hashes (multiple tx) key, wait for the transactions to complete before removing the pending actions\n if (tx) {\n // Wait for all tx events to be reduced\n updateComponent(Action, action.entity, { state: ActionState.WaitingForTxEvents, txHash: tx });\n await awaitStreamValue(txReduced$, (v) => v === tx);\n updateComponent(Action, action.entity, { state: ActionState.TxReduced });\n // TODO: extend ActionData type to be aware of whether waitForTransaction is set\n if (action.awaitConfirmation) {\n if (waitForTransaction) {\n await waitForTransaction(tx);\n } else {\n throw new Error(\"action has awaitConfirmation but no waitForTransaction specified in createActionSystem\");\n }\n }\n }\n\n updateComponent(Action, action.entity, { state: ActionState.Complete });\n } catch (e) {\n handleError(e, action);\n }\n\n // After the action is done executing (failed or completed), remove its actionData and remove the Action component\n remove(action.id);\n }\n\n // Set the action's state to ActionState.Failed\n function handleError(e: unknown, action: ActionData) {\n console.error(e);\n updateComponent(Action, action.entity, { state: ActionState.Failed });\n remove(action.id);\n }\n\n /**\n * Cancels the action with the given ID if it is in the \"Requested\" state.\n * @param actionId ID of the action to be cancelled\n * @returns void\n */\n function cancel(actionId: string): boolean {\n const action = actionData.get(actionId);\n if (!action || getComponentValue(Action, action.entity)?.state !== ActionState.Requested) {\n console.warn(`Action ${actionId} was not found or is not in the \"Requested\" state.`);\n return false;\n }\n updateComponent(Action, action.entity, { state: ActionState.Cancelled });\n remove(actionId);\n return true;\n }\n\n /**\n * Removes actionData disposer of the action with the given ID and removes its pending updates.\n * @param actionId ID of the action to be removed\n */\n function remove(actionId: string) {\n const action = actionData.get(actionId);\n if (!action) {\n console.warn(`Trying to remove action ${actionId} that does not exist.`);\n return;\n }\n\n // Remove this action's pending updates\n const actionEntity = actionId as Entity;\n const overrides = (actionEntity != null && getComponentValue(Action, actionEntity)?.overrides) || [];\n for (const override of overrides) {\n const [id, componentId] = override.split(\"/\");\n const component = componentsWithOptimisticUpdates[componentId];\n component.removeOverride(id);\n }\n\n // Remove this action's autorun and corresponding disposer\n disposer.get(actionId)?.dispose();\n disposer.delete(actionId);\n\n // Remove the action data\n actionData.delete(actionId);\n\n // Remove the action entity after some time\n actionEntity != null && setTimeout(() => world.deleteEntity(actionEntity), 5000);\n }\n\n return { add, cancel, withOptimisticUpdates, Action };\n}\n","import { defineComponent } from \"../Component\";\nimport { Type } from \"../constants\";\nimport { World, Component, SchemaOf, Metadata } from \"../types\";\n\nexport function defineActionComponent<T = unknown>(world: World) {\n const Action = defineComponent(\n world,\n {\n state: Type.String,\n on: Type.OptionalEntity,\n metadata: Type.OptionalT,\n overrides: Type.OptionalStringArray,\n txHash: Type.OptionalString,\n },\n { id: \"Action\" },\n );\n return Action as Component<SchemaOf<typeof Action>, Metadata, T>;\n}\n"],"mappings":"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","names":["ActionState","merge","mapObject","awaitStreamValue","uuid","defineActionComponent","world","defineComponent","createActionSystem","world","txReduced$","waitForTransaction","Action","defineActionComponent","componentsWithOptimisticUpdates","actionData","disposer","dispose","withOptimisticUpdates","component","optimisticComponent","overridableComponent","add","actionRequest","entity","createEntity","setComponent","action","mapObject","c","subscription","merge","checkRequirement","getComponentValue","requirementResult","executeAction","updateComponent","overrides","o","uuid","value","id","tx","awaitStreamValue","v","handleError","remove","e","cancel","actionId","actionEntity","override","componentId"]}
|
package/dist/index.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { S as Schema, M as Metadata, W as World, C as Component, E as Entity, a as ComponentValue, I as Indexer, O as OverridableComponent, b as EntitySymbol, Q as QueryFragment, c as ComponentUpdate, U as UpdateType, H as HasQueryFragment, N as NotQueryFragment, d as HasValueQueryFragment, e as NotValueQueryFragment, T as Type, P as ProxyReadQueryFragment, f as ProxyExpandQueryFragment } from './types-
|
2
|
-
export { i as AnyComponent, A as AnyComponentValue, r as ArrayType, h as ComponentWithStream, g as Components, k as EntityQueryFragment, v as EntityType, L as Layer, x as Layers, t as NumberType, p as OptionalType, y as OptionalTypes, o as Override, j as QueryFragmentType, m as QueryFragments, n as SchemaOf, l as SettingQueryFragment, V as ValueType, s as isArrayType, w as isEntityType, u as isNumberType, q as isOptionalType } from './types-
|
1
|
+
import { S as Schema, M as Metadata, W as World, C as Component, E as Entity, a as ComponentValue, I as Indexer, O as OverridableComponent, b as EntitySymbol, Q as QueryFragment, c as ComponentUpdate, U as UpdateType, H as HasQueryFragment, N as NotQueryFragment, d as HasValueQueryFragment, e as NotValueQueryFragment, T as Type, P as ProxyReadQueryFragment, f as ProxyExpandQueryFragment } from './types-CAQycjNT.js';
|
2
|
+
export { i as AnyComponent, A as AnyComponentValue, r as ArrayType, h as ComponentWithStream, g as Components, k as EntityQueryFragment, v as EntityType, L as Layer, x as Layers, t as NumberType, p as OptionalType, y as OptionalTypes, o as Override, j as QueryFragmentType, m as QueryFragments, n as SchemaOf, l as SettingQueryFragment, V as ValueType, s as isArrayType, w as isEntityType, u as isNumberType, q as isOptionalType } from './types-CAQycjNT.js';
|
3
3
|
import * as rxjs from 'rxjs';
|
4
4
|
import { Observable } from 'rxjs';
|
5
5
|
import { ObservableSet } from 'mobx';
|
@@ -603,4 +603,4 @@ declare function isIndexer<S extends Schema>(c: Component<S> | Indexer<S>): c is
|
|
603
603
|
*/
|
604
604
|
declare function isFullComponentValue<S extends Schema>(component: Component<S>, value: Partial<ComponentValue<S>>): value is ComponentValue<S>;
|
605
605
|
|
606
|
-
export { Component, ComponentMutationOptions, ComponentUpdate, ComponentValue, Entity, EntitySymbol, Has, HasQueryFragment, HasValue, HasValueQueryFragment, Indexer, Metadata, Not, NotQueryFragment, NotValue, NotValueQueryFragment, OverridableComponent, ProxyExpand, ProxyExpandQueryFragment, ProxyRead, ProxyReadQueryFragment, QueryFragment, Schema, Type, UpdateType, World, clearLocalCache, componentValueEquals, createEntity, createIndexer, createLocalCache, createWorld, defineComponent, defineComponentSystem, defineEnterQuery, defineEnterSystem, defineExitQuery, defineExitSystem, defineQuery, defineRxSystem, defineSyncSystem, defineSystem, defineUpdateQuery, defineUpdateSystem, getChildEntities, getComponentEntities, getComponentValue, getComponentValueStrict, getEntitiesWithValue, getEntityComponents, getEntityString, getEntitySymbol, hasComponent, isComponentUpdate, isFullComponentValue, isIndexer, namespaceWorld, overridableComponent, removeComponent, runQuery, setComponent, toUpdate, toUpdateStream, updateComponent, withValue };
|
606
|
+
export { Component, type ComponentMutationOptions, ComponentUpdate, ComponentValue, Entity, EntitySymbol, Has, HasQueryFragment, HasValue, HasValueQueryFragment, Indexer, Metadata, Not, NotQueryFragment, NotValue, NotValueQueryFragment, OverridableComponent, ProxyExpand, ProxyExpandQueryFragment, ProxyRead, ProxyReadQueryFragment, QueryFragment, Schema, Type, UpdateType, World, clearLocalCache, componentValueEquals, createEntity, createIndexer, createLocalCache, createWorld, defineComponent, defineComponentSystem, defineEnterQuery, defineEnterSystem, defineExitQuery, defineExitSystem, defineQuery, defineRxSystem, defineSyncSystem, defineSystem, defineUpdateQuery, defineUpdateSystem, getChildEntities, getComponentEntities, getComponentValue, getComponentValueStrict, getEntitiesWithValue, getEntityComponents, getEntityString, getEntitySymbol, hasComponent, isComponentUpdate, isFullComponentValue, isIndexer, namespaceWorld, overridableComponent, removeComponent, runQuery, setComponent, toUpdate, toUpdateStream, updateComponent, withValue };
|
package/dist/index.js
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
import{a as W,b as v,c as te,d as ne,e as g,f as V,g as re,h as oe,i as ie,j as S,k as pe,l as ae,m as ye,n as C,o as ue,p as E,q as s,r as l,s as me,t as h,u as de,v as O,w as b,x as se,y as le,z as ce}from"./chunk-
|
1
|
+
import{a as W,b as v,c as te,d as ne,e as g,f as V,g as re,h as oe,i as ie,j as S,k as pe,l as ae,m as ye,n as C,o as ue,p as E,q as s,r as l,s as me,t as h,u as de,v as O,w as b,x as se,y as le,z as ce}from"./chunk-2YR7WENB.js";import{concat as K,EMPTY as X,from as Z}from"rxjs";import{filterNullish as R}from"@latticexyz/utils";import{observable as B}from"mobx";import{concat as $,concatMap as q,filter as F,from as D,map as j,merge as L,of as z,share as Y}from"rxjs";var N=(o=>(o[o.Has=0]="Has",o[o.HasValue=1]="HasValue",o[o.Not=2]="Not",o[o.NotValue=3]="NotValue",o[o.ProxyRead=4]="ProxyRead",o[o.ProxyExpand=5]="ProxyExpand",o))(N||{});function xe(e){return[2,4,6,14,16,8,10,12].includes(e)}function Te(e){return[7,8,9,10,11,12,15,16].includes(e)}function ge(e){return[1,2].includes(e)}function Se(e){return[13,14].includes(e)}function Le(e){return{type:0,component:e}}function ze(e){return{type:2,component:e}}function Ye(e,t){return{type:1,component:e,value:t}}function Ge(e,t){return{type:3,component:e,value:t}}function Je(e,t){return{type:4,component:e,depth:t}}function Ke(e,t){return{type:5,component:e,depth:t}}function c(e,t){if(t.type===0)return s(t.component,e);if(t.type===1)return h(t.value,l(t.component,e));if(t.type===2)return!s(t.component,e);if(t.type===3)return!h(t.value,l(t.component,e));throw new Error("Unknown query fragment")}function G(e){return e.type===0||e.type==1}function P(e){return e.type===2||e.type==3}function J(e){return e.type===5||e.type==4}function U(e,t){return e&&G(t)||!e&&P(t)}function I(e,t,n){let r=e,a=!1;for(let y=0;y<n.depth;y++){let o=l(n.component,r);if(!o)return null;let p=o.value;if(!p)return null;if(r=p,a=c(r,t),U(a,t))return a}return a}function Q(e,t,n){if(n===0)return new Set;let r=O(t,{value:e});if(n===1)return r;let a=[...r].map(y=>[...Q(y,t,n-1)]).flat();return new Set([...r,...a])}function A(e,t){let n=t?new Set([...t]):void 0,r,a;for(let y=0;y<e.length;y++){let o=e[y];if(J(o))o.type===4&&(r=o),o.type===5&&(a=o);else if(n)for(let p of[...n]){let m=c(p,o);if(r&&r.depth>0&&!U(m,o)&&(m=I(p,o,r)??m),m||n.delete(p),a&&a.depth>0){let d=Q(p,a.component,a.depth);for(let i of d)(c(i,o)||r&&r.depth>0&&I(i,o,r))&&n.add(i)}}else{if(P(o))throw new Error("First EntityQueryFragment must be Has or HasValue");if(n=o.type===0?new Set([...b(o.component)]):O(o.component,o.value),a&&a.depth>0)for(let p of[...n])for(let m of Q(p,a.component,a.depth))n.add(m)}}return n??new Set}function f(e,t){let n=t?.runOnInit||t?.initialSet?A(e,t.initialSet):new Set,r=B(n),a=D(r).pipe(S(e[0].component)),y=e.findIndex(p=>[5,4].includes(p.type))!==-1,o=L(...e.map(p=>p.component.update$)).pipe(y?q(p=>{let m=A(e,t?.initialSet),d=[];for(let i of r)m.has(i)||(r.delete(i),d.push({entity:i,type:1,component:p.component,value:[void 0,void 0]}));for(let i of m)r.has(i)?d.push({entity:i,type:2,component:p.component,value:[l(p.component,i),void 0]}):(r.add(i),d.push({entity:i,type:0,component:p.component,value:[l(p.component,i),void 0]}));return z(...d)}):j(p=>{if(r.has(p.entity))return e.filter(u=>u.component.id===p.component.id).every(u=>c(p.entity,u))?{...p,type:2}:(r.delete(p.entity),{...p,type:1});if(e.every(d=>c(p.entity,d)))return r.add(p.entity),{...p,type:0}}),R());return{matching:r,update$:$(a,o).pipe(Y())}}function H(e,t){return f(e,t).update$.pipe(F(n=>n.type===2))}function w(e,t){return f(e,t).update$.pipe(F(n=>n.type===0))}function k(e,t){return f(e,t).update$.pipe(F(n=>n.type===1))}function x(e,t,n){let r=t.subscribe(n);e.registerDisposer(()=>r?.unsubscribe())}function ot(e,t,n,r={runOnInit:!0}){x(e,H(t,r),n)}function it(e,t,n,r={runOnInit:!0}){x(e,w(t,r),n)}function pt(e,t,n,r={runOnInit:!0}){x(e,k(t,r),n)}function _(e,t,n,r={runOnInit:!0}){x(e,f(t,r).update$,n)}function at(e,t,n,r={runOnInit:!0}){let a=r?.runOnInit?Z(b(t)).pipe(S(t)):X;x(e,K(a,t.update$),n)}function yt(e,t,n,r,a={update:!1,runOnInit:!0}){_(e,t,({entity:y,type:o})=>{o===0&&C(n(y),y,r(y)),o===1&&E(n(y),y),a?.update&&o===2&&C(n(y),y,r(y))},a)}import{transformIterator as ee}from"@latticexyz/utils";function lt(){let e=new Set,t=[],n=[];function r({id:i,idSuffix:u}={}){let T=i||e.size+(u?"-"+u:""),M=g(T);return e.add(M),T}function a(){return ee(e.values(),V)}function y(i){t.push(i)}function o(i){for(let[,u]of n.filter(T=>!i||T[0]===i))u();n=n.filter(u=>i&&u[0]!==i)}function p(i,u=""){n.push([u,i])}function m(i){let u=g(i);return e.has(u)}function d(i){for(let u of t)s(u,i)&&E(u,i);e.delete(g(i))}return{registerEntity:r,components:t,registerComponent:y,dispose:o,registerDisposer:p,hasEntity:m,getEntities:a,entitySymbols:e,deleteEntity:d}}function ct(e,t){return{...e,registerDisposer:n=>e.registerDisposer(n,t),dispose:()=>e.dispose(t)}}function ft(e,t){return e.components.filter(n=>s(n,t))}export{Le as Has,Ye as HasValue,ze as Not,Ge as NotValue,te as OptionalTypes,Ke as ProxyExpand,Je as ProxyRead,N as QueryFragmentType,W as Type,v as UpdateType,le as clearLocalCache,h as componentValueEquals,ne as createEntity,re as createIndexer,ce as createLocalCache,lt as createWorld,ye as defineComponent,at as defineComponentSystem,w as defineEnterQuery,it as defineEnterSystem,k as defineExitQuery,pt as defineExitSystem,f as defineQuery,x as defineRxSystem,yt as defineSyncSystem,_ as defineSystem,H as defineUpdateQuery,ot as defineUpdateSystem,Q as getChildEntities,b as getComponentEntities,l as getComponentValue,me as getComponentValueStrict,O as getEntitiesWithValue,ft as getEntityComponents,V as getEntityString,g as getEntitySymbol,s as hasComponent,Te as isArrayType,oe as isComponentUpdate,Se as isEntityType,ae as isFullComponentValue,pe as isIndexer,ge as isNumberType,xe as isOptionalType,ct as namespaceWorld,se as overridableComponent,E as removeComponent,A as runQuery,C as setComponent,ie as toUpdate,S as toUpdateStream,ue as updateComponent,de as withValue};
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/System.ts","../src/Query.ts","../src/types.ts","../src/World.ts"],"sourcesContent":["import { concat, EMPTY, from, Observable } from \"rxjs\";\nimport { getComponentEntities, removeComponent, setComponent } from \"./Component\";\nimport { UpdateType } from \"./constants\";\nimport { defineEnterQuery, defineExitQuery, defineQuery, defineUpdateQuery } from \"./Query\";\nimport { Component, ComponentUpdate, ComponentValue, Entity, QueryFragment, Schema, World } from \"./types\";\nimport { toUpdateStream } from \"./utils\";\n\n/**\n * Create a system that is called on every update of the given observable.\n *\n * @remarks\n * Advantage of using this function over directly subscribing to the RxJS observable is that the system is registered in the `world` and\n * disposed when the `world` is disposed (eg. during a hot reload in development).\n *\n * @param world {@link World} object this system should be registered in.\n * @param observable$ Observable to react to.\n * @param system System function to run on updates of the `observable$`. System function gets passed the update events from the `observable$`.\n */\nexport function defineRxSystem<T>(world: World, observable$: Observable<T>, system: (event: T) => void) {\n const subscription = observable$.subscribe(system);\n world.registerDisposer(() => subscription?.unsubscribe());\n}\n\n/**\n * Create a system that is called on every event of the given {@link defineUpdateQuery update query}.\n *\n * @param world {@link World} object this system should be registered in.\n * @param query Update query to react to.\n * @param system System function to run when the result of the given update query changes.\n * @param options Optional: {\n * runOnInit: if true, run this system for all entities matching the query when the system is created.\n * Else only run on updates after the system is created. Default true.\n * }\n */\nexport function defineUpdateSystem(\n world: World,\n query: QueryFragment[],\n system: (update: ComponentUpdate) => void,\n options: { runOnInit?: boolean } = { runOnInit: true },\n) {\n defineRxSystem(world, defineUpdateQuery(query, options), system);\n}\n\n/**\n * Create a system that is called on every event of the given {@link defineEnterQuery enter query}.\n *\n * @param world {@link World} object this system should be registered in.\n * @param query Enter query to react to.\n * @param system System function to run when the result of the given enter query changes.\n * @param options Optional: {\n * runOnInit: if true, run this system for all entities matching the query when the system is created.\n * Else only run on updates after the system is created. Default true.\n * }\n */\nexport function defineEnterSystem(\n world: World,\n query: QueryFragment[],\n system: (update: ComponentUpdate) => void,\n options: { runOnInit?: boolean } = { runOnInit: true },\n) {\n defineRxSystem(world, defineEnterQuery(query, options), system);\n}\n\n/**\n * Create a system that is called on every event of the given {@link defineExitQuery exit query}.\n *\n * @param world {@link World} object this system should be registered in.\n * @param query Exit query to react to.\n * @param system System function to run when the result of the given exit query changes.\n * @param options Optional: {\n * runOnInit: if true, run this system for all entities matching the query when the system is created.\n * Else only run on updates after the system is created. Default true.\n * }\n */\nexport function defineExitSystem(\n world: World,\n query: QueryFragment[],\n system: (update: ComponentUpdate) => void,\n options: { runOnInit?: boolean } = { runOnInit: true },\n) {\n defineRxSystem(world, defineExitQuery(query, options), system);\n}\n\n/**\n * Create a system that is called on every event of the given {@link defineQuery query}.\n *\n * @param world {@link World} object this system should be registered in.\n * @param query Query to react to.\n * @param system System function to run when the result of the given query changes.\n * @param options Optional: {\n * runOnInit: if true, run this system for all entities matching the query when the system is created.\n * Else only run on updates after the system is created. Default true.\n * }\n */\nexport function defineSystem(\n world: World,\n query: QueryFragment[],\n system: (update: ComponentUpdate & { type: UpdateType }) => void,\n options: { runOnInit?: boolean } = { runOnInit: true },\n) {\n defineRxSystem(world, defineQuery(query, options).update$, system);\n}\n\n/**\n * Create a system that is called every time the given component is updated.\n *\n * @param world {@link World} object this system should be registered in.\n * @param component Component to whose updates to react.\n * @param system System function to run when the given component is updated.\n * @param options Optional: {\n * runOnInit: if true, run this system for all entities in the component when the system is created.\n * Else only run on updates after the system is created. Default true.\n * }\n */\nexport function defineComponentSystem<S extends Schema>(\n world: World,\n component: Component<S>,\n system: (update: ComponentUpdate<S>) => void,\n options: { runOnInit?: boolean } = { runOnInit: true },\n) {\n const initial$ = options?.runOnInit ? from(getComponentEntities(component)).pipe(toUpdateStream(component)) : EMPTY;\n defineRxSystem(world, concat(initial$, component.update$), system);\n}\n\n/**\n * Create a system to synchronize updates to one component with another component.\n *\n * @param world {@link World} object this system should be registered in.\n * @param query Result of `component` is added to all entites matching this query.\n * @param component Function returning the component to be added to all entities matching the given query.\n * @param value Function returning the component value to be added to all entities matching the given query.\n */\nexport function defineSyncSystem<T extends Schema>(\n world: World,\n query: QueryFragment[],\n component: (entity: Entity) => Component<T>,\n value: (entity: Entity) => ComponentValue<T>,\n options: { update?: boolean; runOnInit?: boolean } = { update: false, runOnInit: true },\n) {\n defineSystem(\n world,\n query,\n ({ entity, type }) => {\n if (type === UpdateType.Enter) setComponent(component(entity), entity, value(entity));\n if (type === UpdateType.Exit) removeComponent(component(entity), entity);\n if (options?.update && type === UpdateType.Update) setComponent(component(entity), entity, value(entity));\n },\n options,\n );\n}\n","import { filterNullish } from \"@latticexyz/utils\";\nimport { observable, ObservableSet } from \"mobx\";\nimport { concat, concatMap, filter, from, map, merge, Observable, of, share } from \"rxjs\";\nimport {\n componentValueEquals,\n getComponentEntities,\n getComponentValue,\n getEntitiesWithValue,\n hasComponent,\n} from \"./Component\";\nimport { UpdateType, Type } from \"./constants\";\nimport {\n Component,\n ComponentUpdate,\n ComponentValue,\n Entity,\n EntityQueryFragment,\n HasQueryFragment,\n HasValueQueryFragment,\n NotQueryFragment,\n NotValueQueryFragment,\n ProxyExpandQueryFragment,\n ProxyReadQueryFragment,\n QueryFragment,\n QueryFragmentType,\n Schema,\n SettingQueryFragment,\n} from \"./types\";\nimport { toUpdateStream } from \"./utils\";\n\n/**\n * Create a {@link HasQueryFragment}.\n *\n * @remarks\n * The {@link HasQueryFragment} filters for entities that have the given component,\n * independent from the component value.\n *\n * @example\n * Query for all entities with a `Position`.\n * ```\n * runQuery([Has(Position)]);\n * ```\n *\n * @param component Component this query fragment refers to.\n * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.\n */\nexport function Has<T extends Schema>(component: Component<T>): HasQueryFragment<T> {\n return { type: QueryFragmentType.Has, component };\n}\n\n/**\n * Create a {@link NotQueryFragment}.\n *\n * @remarks\n * The {@link NotQueryFragment} filters for entities that don't have the given component,\n * independent from the component value.\n *\n * @example\n * Query for all entities with a `Position` that are not `Movable`.\n * ```\n * runQuery([Has(Position), Not(Movable)]);\n * ```\n *\n * @param component Component this query fragment refers to.\n * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.\n */\nexport function Not<T extends Schema>(component: Component<T>): NotQueryFragment<T> {\n return { type: QueryFragmentType.Not, component };\n}\n\n/**\n * Create a {@link HasValueQueryFragment}.\n *\n * @remarks\n * The {@link HasValueQueryFragment} filters for entities that have the given component\n * with the given component value.\n *\n * @example\n * Query for all entities at Position (0,0).\n * ```\n * runQuery([HasValue(Position, { x: 0, y: 0 })]);\n * ```\n *\n * @param component Component this query fragment refers to.\n * @param value Only include entities with this (partial) component value from the result.\n * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.\n */\nexport function HasValue<T extends Schema>(\n component: Component<T>,\n value: Partial<ComponentValue<T>>,\n): HasValueQueryFragment<T> {\n return { type: QueryFragmentType.HasValue, component, value };\n}\n\n/**\n * Create a {@link NotValueQueryFragment}.\n *\n * @remarks\n * The {@link NotValueQueryFragment} filters for entities that don't have the given component\n * with the given component value.\n *\n * @example\n * Query for all entities that have a `Position`, except for those at `Position` (0,0).\n * ```\n * runQuery([Has(Position), NotValue(Position, { x: 0, y: 0 })]);\n * ```\n *\n * @param component Component this query fragment refers to.\n * @param value Exclude entities with this (partial) component value from the result.\n * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.\n */\nexport function NotValue<T extends Schema>(\n component: Component<T>,\n value: Partial<ComponentValue<T>>,\n): NotValueQueryFragment<T> {\n return { type: QueryFragmentType.NotValue, component, value };\n}\n\n/**\n * Create a {@link ProxyReadQueryFragment}.\n *\n * @remarks\n * The {@link ProxyReadQueryFragment} activates the \"proxy read mode\" for the rest of the query.\n * This means that for all remaining fragments in the query not only the entities themselves are checked, but also\n * their \"ancestors\" up to the given `depth` on the relationship chain defined by the given `component`.\n *\n * @example\n * Query for all entities that have a `Position` and are (directly or indirectly) owned by an entity with `Name` \"Alice\".\n * ```\n * runQuery([Has(Position), ProxyRead(OwnedByEntity, Number.MAX_SAFE_INTEGER), HasValue(Name, { name: \"Alice\" })]);\n * ```\n *\n * @param component Component this query fragment refers to.\n * @param depth Max depth in the relationship chain to traverse.\n * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.\n */\nexport function ProxyRead(component: Component<{ value: Type.Entity }>, depth: number): ProxyReadQueryFragment {\n return { type: QueryFragmentType.ProxyRead, component, depth };\n}\n\n/**\n * Create a {@link ProxyExpandQueryFragment}.\n *\n * @remarks\n * The {@link ProxyExpandQueryFragment} activates the \"proxy expand mode\" for the rest of the query.\n * This means that for all remaining fragments in the query not only the matching entities themselves are included in the intermediate set,\n * but also all their \"children\" down to the given `depth` on the relationship chain defined by the given `component`.\n *\n * @example\n * Query for all entities (directly or indirectly) owned by an entity with `Name` \"Alice\".\n * ```\n * runQuery([ProxyExpand(OwnedByEntity, Number.MAX_SAFE_INTEGER), HasValue(Name, { name: \"Alice\" })]);\n * ```\n *\n * @param component Component to apply this query fragment to.\n * @param depth Max depth in the relationship chain to traverse.\n * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.\n */\nexport function ProxyExpand(component: Component<{ value: Type.Entity }>, depth: number): ProxyExpandQueryFragment {\n return { type: QueryFragmentType.ProxyExpand, component, depth };\n}\n\n/**\n * Helper function to check whether a given entity passes a given query fragment.\n *\n * @param entity Entity to check.\n * @param fragment Query fragment to check.\n * @returns True if the entity passes the query fragment, else false.\n */\nfunction passesQueryFragment<T extends Schema>(entity: Entity, fragment: EntityQueryFragment<T>): boolean {\n if (fragment.type === QueryFragmentType.Has) {\n // Entity must have the given component\n return hasComponent(fragment.component, entity);\n }\n\n if (fragment.type === QueryFragmentType.HasValue) {\n // Entity must have the given component value\n return componentValueEquals(fragment.value, getComponentValue(fragment.component, entity));\n }\n\n if (fragment.type === QueryFragmentType.Not) {\n // Entity must not have the given component\n return !hasComponent(fragment.component, entity);\n }\n\n if (fragment.type === QueryFragmentType.NotValue) {\n // Entity must not have the given component value\n return !componentValueEquals(fragment.value, getComponentValue(fragment.component, entity));\n }\n\n throw new Error(\"Unknown query fragment\");\n}\n\n/**\n * Helper function to check whether a query fragment is \"positive\" (ie `Has` or `HasValue`)\n *\n * @param fragment Query fragment to check.\n * @returns True if the query fragment is positive, else false.\n */\nfunction isPositiveFragment<T extends Schema>(\n fragment: QueryFragment<T>,\n): fragment is HasQueryFragment<T> | HasValueQueryFragment<T> {\n return fragment.type === QueryFragmentType.Has || fragment.type == QueryFragmentType.HasValue;\n}\n\n/**\n * Helper function to check whether a query fragment is \"negative\" (ie `Not` or `NotValue`)\n *\n * @param fragment Query fragment to check.\n * @returns True if the query fragment is negative, else false.\n */\nfunction isNegativeFragment<T extends Schema>(\n fragment: QueryFragment<T>,\n): fragment is NotQueryFragment<T> | NotValueQueryFragment<T> {\n return fragment.type === QueryFragmentType.Not || fragment.type == QueryFragmentType.NotValue;\n}\n\n/**\n * Helper function to check whether a query fragment is a setting fragment (ie `ProxyExpand` or `ProxyRead`)\n *\n * @param fragment Query fragment to check.\n * @returns True if the query fragment is a setting fragment, else false.\n */\nfunction isSettingFragment<T extends Schema>(fragment: QueryFragment<T>): fragment is SettingQueryFragment {\n return fragment.type === QueryFragmentType.ProxyExpand || fragment.type == QueryFragmentType.ProxyRead;\n}\n\n/**\n * Helper function to check whether the result of a query pass check is a breaking state.\n *\n * @remarks\n * For positive fragments (Has/HasValue) we need to find any passing entity up the proxy chain\n * so as soon as passes is true, we can early return. For negative fragments (Not/NotValue) every entity\n * up the proxy chain must pass, so we can early return if we find one that doesn't pass.\n *\n * @param passes Boolean result of previous query pass check.\n * @param fragment Fragment that was used in the query pass check.\n * @returns True if the result is breaking pass state, else false.\n */\nfunction isBreakingPassState(passes: boolean, fragment: EntityQueryFragment<Schema>) {\n return (passes && isPositiveFragment(fragment)) || (!passes && isNegativeFragment(fragment));\n}\n\n/**\n * Helper function to check whether an entity passes a query fragment when taking into account a {@link ProxyReadQueryFragment}.\n *\n * @param entity {@link Entity} of the entity to check.\n * @param fragment Query fragment to check.\n * @param proxyRead {@link ProxyReadQueryFragment} to take into account.\n * @returns True if the entity passes the query fragment, else false.\n */\nfunction passesQueryFragmentProxy<T extends Schema>(\n entity: Entity,\n fragment: EntityQueryFragment<T>,\n proxyRead: ProxyReadQueryFragment,\n): boolean | null {\n let proxyEntity = entity;\n let passes = false;\n for (let i = 0; i < proxyRead.depth; i++) {\n const value = getComponentValue(proxyRead.component, proxyEntity);\n // If the current entity does not have the proxy component, abort\n if (!value) return null;\n\n const entity = value.value;\n if (!entity) return null;\n\n // Move up the proxy chain\n proxyEntity = entity;\n passes = passesQueryFragment(proxyEntity, fragment);\n\n if (isBreakingPassState(passes, fragment)) {\n return passes;\n }\n }\n return passes;\n}\n\n/**\n * Recursively compute all direct and indirect child entities up to the specified depth\n * down the relationship chain defined by the given component.\n *\n * @param entity Entity to get all child entities for up to the specified depth\n * @param component Component to use for the relationship chain.\n * @param depth Depth up to which the recursion should be applied.\n * @returns Set of entities that are child entities of the given entity via the given component.\n */\nexport function getChildEntities(\n entity: Entity,\n component: Component<{ value: Type.Entity }>,\n depth: number,\n): Set<Entity> {\n if (depth === 0) return new Set();\n\n const directChildEntities = getEntitiesWithValue(component, { value: entity });\n if (depth === 1) return directChildEntities;\n\n const indirectChildEntities = [...directChildEntities]\n .map((childEntity) => [...getChildEntities(childEntity, component, depth - 1)])\n .flat();\n\n return new Set([...directChildEntities, ...indirectChildEntities]);\n}\n\n/**\n * Execute a list of query fragments to receive a Set of matching entities.\n *\n * @remarks\n * The query fragments are executed from left to right and are concatenated with a logical `AND`.\n * For performance reasons, the most restrictive query fragment should be first in the list of query fragments,\n * in order to reduce the number of entities the next query fragment needs to be checked for.\n * If no proxy fragments are used, every entity in the resulting set passes every query fragment.\n * If setting fragments are used, the order of the query fragments influences the result, since settings only apply to\n * fragments after the setting fragment.\n *\n * @param fragments Query fragments to execute.\n * @param initialSet Optional: provide a Set of entities to execute the query on. If none is given, all existing entities are used for the query.\n * @returns Set of entities matching the query fragments.\n */\nexport function runQuery(fragments: QueryFragment[], initialSet?: Set<Entity>): Set<Entity> {\n let entities: Set<Entity> | undefined = initialSet ? new Set([...initialSet]) : undefined; // Copy to a fresh set because it will be modified in place\n let proxyRead: ProxyReadQueryFragment | undefined = undefined;\n let proxyExpand: ProxyExpandQueryFragment | undefined = undefined;\n\n // Process fragments\n for (let i = 0; i < fragments.length; i++) {\n const fragment = fragments[i];\n if (isSettingFragment(fragment)) {\n // Store setting fragments for subsequent query fragments\n if (fragment.type === QueryFragmentType.ProxyRead) proxyRead = fragment;\n if (fragment.type === QueryFragmentType.ProxyExpand) proxyExpand = fragment;\n } else if (!entities) {\n // Handle entity query fragments\n // First regular fragment must be Has or HasValue\n if (isNegativeFragment(fragment)) {\n throw new Error(\"First EntityQueryFragment must be Has or HasValue\");\n }\n\n // Create the first interim result\n entities =\n fragment.type === QueryFragmentType.Has\n ? new Set([...getComponentEntities(fragment.component)])\n : getEntitiesWithValue(fragment.component, fragment.value);\n\n // Add entity's children up to the specified depth if proxy expand is active\n if (proxyExpand && proxyExpand.depth > 0) {\n for (const entity of [...entities]) {\n for (const childEntity of getChildEntities(entity, proxyExpand.component, proxyExpand.depth)) {\n entities.add(childEntity);\n }\n }\n }\n } else {\n // There already is an interim result, apply the current fragment\n for (const entity of [...entities]) {\n // Branch 1: Simple / check if the current entity passes the query fragment\n let passes = passesQueryFragment(entity, fragment);\n\n // Branch 2: Proxy upwards / check if proxy entity passes the query\n if (proxyRead && proxyRead.depth > 0 && !isBreakingPassState(passes, fragment)) {\n passes = passesQueryFragmentProxy(entity, fragment, proxyRead) ?? passes;\n }\n\n // If the entity didn't pass the query fragment, remove it from the interim set\n if (!passes) entities.delete(entity);\n\n // Branch 3: Proxy downwards / run the query fragments on child entities if proxy expand is active\n if (proxyExpand && proxyExpand.depth > 0) {\n const childEntities = getChildEntities(entity, proxyExpand.component, proxyExpand.depth);\n for (const childEntity of childEntities) {\n // Add the child entity if it passes the direct check\n // or if a proxy read is active and it passes the proxy read check\n if (\n passesQueryFragment(childEntity, fragment) ||\n (proxyRead && proxyRead.depth > 0 && passesQueryFragmentProxy(childEntity, fragment, proxyRead))\n )\n entities.add(childEntity);\n }\n }\n }\n }\n }\n\n return entities ?? new Set<Entity>();\n}\n\n/**\n * Create a query object including an update$ stream and a Set of entities currently matching the query.\n *\n * @remarks\n * `update$` stream needs to be subscribed to in order for the logic inside the stream to be executed and therefore\n * in order for the `matching` set to be updated.\n *\n * `defineQuery` should be strongly preferred over `runQuery` if the query is used for systems or other\n * use cases that repeatedly require the query result or updates to the query result. `defineQuery` does not\n * reevaluate the entire query if an accessed component changes, but only performs the minimal set of checks\n * on the updated entity to evaluate wether the entity still matches the query, resulting in significant performance\n * advantages over `runQuery`.\n *\n * The query fragments are executed from left to right and are concatenated with a logical `AND`.\n * For performance reasons, the most restrictive query fragment should be first in the list of query fragments,\n * in order to reduce the number of entities the next query fragment needs to be checked for.\n * If no proxy fragments are used, every entity in the resulting set passes every query fragment.\n * If setting fragments are used, the order of the query fragments influences the result, since settings only apply to\n * fragments after the setting fragment.\n *\n * @param fragments Query fragments to execute.\n * @param options Optional: {\n * runOnInit: if true, the query is executed once with `runQuery` to build an iniital Set of matching entities. If false only updates after the query was created are considered.\n * initialSet: if given, this set is passed to `runOnInit` when building the initial Set of matching entities.\n * }\n * @returns Query object: {\n * update$: RxJS stream of updates to the query result. The update contains the component update that caused the query update, as well as the {@link UpdateType update type}.\n * matching: Mobx observable set of entities currently matching the query.\n * }.\n */\nexport function defineQuery(\n fragments: QueryFragment[],\n options?: { runOnInit?: boolean; initialSet?: Set<Entity> },\n): {\n update$: Observable<ComponentUpdate & { type: UpdateType }>;\n matching: ObservableSet<Entity>;\n} {\n const initialSet =\n options?.runOnInit || options?.initialSet ? runQuery(fragments, options.initialSet) : new Set<Entity>();\n\n const matching = observable(initialSet);\n const initial$ = from(matching).pipe(toUpdateStream(fragments[0].component));\n\n const containsProxy =\n fragments.findIndex((v) => [QueryFragmentType.ProxyExpand, QueryFragmentType.ProxyRead].includes(v.type)) !== -1;\n\n const internal$ = merge(...fragments.map((f) => f.component.update$)) // Combine all component update streams accessed accessed in this query\n .pipe(\n containsProxy // Query contains proxies\n ? concatMap((update) => {\n // If the query contains proxy read or expand fragments, entities up or down the proxy chain might match due to this update.\n // We have to run the entire query again and compare the result.\n // TODO: We might be able to make this more efficient by first computing the set of entities that are potentially touched by this update\n // and then only rerun the query on this set.\n const newMatchingSet = runQuery(fragments, options?.initialSet);\n const updates: (ComponentUpdate & { type: UpdateType })[] = [];\n\n for (const previouslyMatchingEntity of matching) {\n // Entity matched before but doesn't match now\n if (!newMatchingSet.has(previouslyMatchingEntity)) {\n matching.delete(previouslyMatchingEntity);\n updates.push({\n entity: previouslyMatchingEntity,\n type: UpdateType.Exit,\n component: update.component,\n value: [undefined, undefined],\n });\n }\n }\n\n for (const matchingEntity of newMatchingSet) {\n if (matching.has(matchingEntity)) {\n // Entity matched before and still matches\n updates.push({\n entity: matchingEntity,\n type: UpdateType.Update,\n component: update.component,\n value: [getComponentValue(update.component, matchingEntity), undefined],\n });\n } else {\n // Entity didn't match before but matches now\n matching.add(matchingEntity);\n updates.push({\n entity: matchingEntity,\n type: UpdateType.Enter,\n component: update.component,\n value: [getComponentValue(update.component, matchingEntity), undefined],\n });\n }\n }\n\n return of(...updates);\n })\n : // Query does not contain proxies\n map((update) => {\n if (matching.has(update.entity)) {\n // If this entity matched the query before, check if it still matches it\n // Find fragments accessign this component (linear search is fine since the number fragments is likely small)\n const relevantFragments = fragments.filter((f) => f.component.id === update.component.id);\n const pass = relevantFragments.every((f) => passesQueryFragment(update.entity, f as EntityQueryFragment)); // We early return if the query contains proxies\n\n if (pass) {\n // Entity passed before and still passes, forward update\n return { ...update, type: UpdateType.Update };\n } else {\n // Entity passed before but not anymore, forward update and exit\n matching.delete(update.entity);\n return { ...update, type: UpdateType.Exit };\n }\n }\n\n // This entity didn't match before, check all fragments\n const pass = fragments.every((f) => passesQueryFragment(update.entity, f as EntityQueryFragment)); // We early return if the query contains proxies\n if (pass) {\n // Entity didn't pass before but passes now, forward update end enter\n matching.add(update.entity);\n return { ...update, type: UpdateType.Enter };\n }\n }),\n filterNullish(),\n );\n\n return {\n matching,\n update$: concat(initial$, internal$).pipe(share()),\n };\n}\n\n/**\n * Define a query object that only passes update events of type {@link UpdateType}.Update to the `update$` stream.\n * See {@link defineQuery} for details.\n *\n * @param fragments Query fragments\n * @returns Stream of component updates of entities that had already matched the query\n */\nexport function defineUpdateQuery(\n fragments: QueryFragment[],\n options?: { runOnInit?: boolean },\n): Observable<ComponentUpdate & { type: UpdateType }> {\n return defineQuery(fragments, options).update$.pipe(filter((e) => e.type === UpdateType.Update));\n}\n\n/**\n * Define a query object that only passes update events of type {@link UpdateType}.Enter to the `update$` stream.\n * See {@link defineQuery} for details.\n *\n * @param fragments Query fragments\n * @returns Stream of component updates of entities matching the query for the first time\n */\nexport function defineEnterQuery(\n fragments: QueryFragment[],\n options?: { runOnInit?: boolean },\n): Observable<ComponentUpdate> {\n return defineQuery(fragments, options).update$.pipe(filter((e) => e.type === UpdateType.Enter));\n}\n\n/**\n * Define a query object that only passes update events of type {@link UpdateType}.Exit to the `update$` stream.\n * See {@link defineQuery} for details.\n *\n * @param fragments Query fragments\n * @returns Stream of component updates of entities not matching the query anymore for the first time\n */\nexport function defineExitQuery(\n fragments: QueryFragment[],\n options?: { runOnInit?: boolean },\n): Observable<ComponentUpdate> {\n return defineQuery(fragments, options).update$.pipe(filter((e) => e.type === UpdateType.Exit));\n}\n","import { Subject } from \"rxjs\";\nimport { Type } from \"./constants\";\nimport { Opaque } from \"type-fest\";\n\n/**\n * Entities are represented as symbols internally for memory efficiency.\n * To get the entity's string representation, use `getEntityString(entitySymbol)`\n */\nexport type EntitySymbol = Opaque<symbol, \"EntitySymbol\">;\n\nexport type Entity = Opaque<string, \"Entity\">;\n\n/**\n * Used to define the schema of a {@link Component}.\n * Uses {@link Type} enum to be able to access the component type in JavaScript as well as have TypeScript type checks.\n */\nexport type Schema = {\n [key: string]: Type;\n};\n\n/**\n * Used to add arbitrary metadata to components.\n */\nexport type Metadata =\n | {\n [key: string]: unknown;\n }\n | undefined;\n\n/**\n * Mapping between JavaScript {@link Type} enum and corresponding TypeScript type.\n */\nexport type ValueType<T = unknown> = {\n [Type.Boolean]: boolean;\n [Type.Number]: number;\n [Type.BigInt]: bigint;\n [Type.String]: string;\n [Type.NumberArray]: number[];\n [Type.BigIntArray]: bigint[];\n [Type.StringArray]: string[];\n [Type.Entity]: Entity;\n [Type.EntityArray]: Entity[];\n [Type.OptionalNumber]: number | undefined;\n [Type.OptionalBigInt]: bigint | undefined;\n [Type.OptionalBigIntArray]: bigint[] | undefined;\n [Type.OptionalString]: string | undefined;\n [Type.OptionalNumberArray]: number[] | undefined;\n [Type.OptionalStringArray]: string[] | undefined;\n [Type.OptionalEntity]: Entity | undefined;\n [Type.OptionalEntityArray]: Entity[] | undefined;\n [Type.T]: T;\n [Type.OptionalT]: T | undefined;\n};\n\n/**\n * Used to infer the TypeScript type of a component value corresponding to a given {@link Schema}.\n */\nexport type ComponentValue<S extends Schema = Schema, T = unknown> = {\n [key in keyof S]: ValueType<T>[S[key]];\n};\n\n/**\n * Type of a component update corresponding to a given {@link Schema}.\n */\nexport type ComponentUpdate<S extends Schema = Schema, T = unknown> = {\n entity: Entity;\n value: [ComponentValue<S, T> | undefined, ComponentValue<S, T> | undefined];\n component: Component<S, Metadata, T>;\n};\n\n/**\n * Type of component returned by {@link defineComponent}.\n */\nexport interface Component<S extends Schema = Schema, M extends Metadata = Metadata, T = unknown> {\n id: string;\n values: { [key in keyof S]: Map<EntitySymbol, ValueType<T>[S[key]]> };\n schema: S;\n metadata: M;\n entities: () => IterableIterator<Entity>;\n world: World;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n update$: Subject<ComponentUpdate<S, T>> & { observers: any };\n}\n\n/**\n * Type of indexer returned by {@link createIndexer}.\n */\nexport type Indexer<S extends Schema, M extends Metadata = Metadata, T = unknown> = Component<S, M, T> & {\n getEntitiesWithValue: (value: ComponentValue<S, T>) => Set<Entity>;\n};\n\nexport type Components = {\n [key: string]: Component;\n};\n\nexport interface ComponentWithStream<S extends Schema, T = unknown> extends Component<S, Metadata, T> {\n stream$: Subject<{ entity: Entity; value: ComponentValue<S, T> | undefined }>;\n}\n\nexport type AnyComponentValue = ComponentValue<Schema>;\n\nexport type AnyComponent = Component<Schema>;\n\n/**\n * Type of World returned by {@link createWorld}.\n */\nexport type World = {\n registerEntity: (options?: { id?: string; idSuffix?: string }) => Entity;\n registerComponent: (component: Component) => void;\n components: Component[];\n getEntities: () => IterableIterator<Entity>;\n dispose: () => void;\n registerDisposer: (disposer: () => void) => void;\n hasEntity: (entity: Entity) => boolean;\n deleteEntity: (entity: Entity) => void;\n entitySymbols: Set<EntitySymbol>;\n};\n\nexport enum QueryFragmentType {\n Has,\n HasValue,\n Not,\n NotValue,\n ProxyRead,\n ProxyExpand,\n}\n\nexport type HasQueryFragment<T extends Schema> = {\n type: QueryFragmentType.Has;\n component: Component<T>;\n};\n\nexport type HasValueQueryFragment<T extends Schema> = {\n type: QueryFragmentType.HasValue;\n component: Component<T>;\n value: Partial<ComponentValue<T>>;\n};\n\nexport type NotQueryFragment<T extends Schema> = {\n type: QueryFragmentType.Not;\n component: Component<T>;\n};\n\nexport type NotValueQueryFragment<T extends Schema> = {\n type: QueryFragmentType.NotValue;\n component: Component<T>;\n value: Partial<ComponentValue<T>>;\n};\n\nexport type ProxyReadQueryFragment = {\n type: QueryFragmentType.ProxyRead;\n component: Component<{ value: Type.Entity }>;\n depth: number;\n};\n\nexport type ProxyExpandQueryFragment = {\n type: QueryFragmentType.ProxyExpand;\n component: Component<{ value: Type.Entity }>;\n depth: number;\n};\n\nexport type QueryFragment<T extends Schema = Schema> =\n | HasQueryFragment<T>\n | HasValueQueryFragment<T>\n | NotQueryFragment<T>\n | NotValueQueryFragment<T>\n | ProxyReadQueryFragment\n | ProxyExpandQueryFragment;\n\nexport type EntityQueryFragment<T extends Schema = Schema> =\n | HasQueryFragment<T>\n | HasValueQueryFragment<T>\n | NotQueryFragment<T>\n | NotValueQueryFragment<T>;\n\nexport type SettingQueryFragment = ProxyReadQueryFragment | ProxyExpandQueryFragment;\n\nexport type QueryFragments = QueryFragment<Schema>[];\n\nexport type SchemaOf<C extends Component<Schema>> = C extends Component<infer S> ? S : never;\n\nexport type Override<S extends Schema, T = unknown> = {\n entity: Entity;\n value: Partial<ComponentValue<S, T>> | null;\n};\n\n/**\n * Type of overridable component returned by {@link overridableComponent}.\n */\nexport type OverridableComponent<S extends Schema = Schema, M extends Metadata = Metadata, T = unknown> = Component<\n S,\n M,\n T\n> & {\n addOverride: (overrideId: string, update: Override<S, T>) => void;\n removeOverride: (overrideId: string) => void;\n};\n\nexport type OptionalType =\n | Type.OptionalNumber\n | Type.OptionalBigInt\n | Type.OptionalString\n | Type.OptionalEntity\n | Type.OptionalNumberArray\n | Type.OptionalBigIntArray\n | Type.OptionalStringArray\n | Type.OptionalEntityArray;\n\nexport function isOptionalType(t: Type): t is OptionalType {\n return [\n Type.OptionalNumber,\n Type.OptionalBigInt,\n Type.OptionalString,\n Type.OptionalEntity,\n Type.OptionalEntityArray,\n Type.OptionalNumberArray,\n Type.OptionalBigIntArray,\n Type.OptionalStringArray,\n ].includes(t);\n}\n\nexport type ArrayType =\n | Type.NumberArray\n | Type.OptionalNumberArray\n | Type.BigIntArray\n | Type.OptionalBigIntArray\n | Type.StringArray\n | Type.OptionalStringArray\n | Type.EntityArray\n | Type.OptionalEntityArray;\n\nexport function isArrayType(t: Type): t is ArrayType {\n return [\n Type.NumberArray,\n Type.OptionalNumberArray,\n Type.BigIntArray,\n Type.OptionalBigIntArray,\n Type.StringArray,\n Type.OptionalStringArray,\n Type.EntityArray,\n Type.OptionalEntityArray,\n ].includes(t);\n}\n\nexport type NumberType = Type.Number | Type.OptionalNumber;\nexport function isNumberType(t: Type): t is NumberType {\n return [Type.Number, Type.OptionalNumber].includes(t);\n}\n\nexport type EntityType = Type.Entity | Type.OptionalEntity;\nexport function isEntityType(t: Type): t is EntityType {\n return [Type.Entity, Type.OptionalEntity].includes(t);\n}\n\nexport type Layer = {\n world: World;\n components: Record<string, Component<Schema>>;\n};\n\nexport type Layers = Record<string, Layer>;\n","import { transformIterator } from \"@latticexyz/utils\";\nimport { hasComponent, removeComponent } from \"./Component\";\nimport { getEntityString, getEntitySymbol } from \"./Entity\";\nimport { Component, Entity, EntitySymbol, World } from \"./types\";\n\n/**\n * Create a new World.\n *\n * @remarks\n * A World is the central object of an ECS application, where all {@link defineComponent Components},\n * {@link registerEntity Entities} and {@link defineSystem Systems} are registerd.\n *\n * @returns A new World\n */\nexport function createWorld() {\n const entitySymbols = new Set<EntitySymbol>();\n const components: Component[] = [];\n let disposers: [string, () => void][] = [];\n\n function registerEntity({ id, idSuffix }: { id?: string; idSuffix?: string } = {}) {\n const entity = (id || entitySymbols.size + (idSuffix ? \"-\" + idSuffix : \"\")) as Entity;\n const entitySymbol = getEntitySymbol(entity);\n\n // Register entity\n entitySymbols.add(entitySymbol);\n\n return entity;\n }\n\n function getEntities() {\n return transformIterator(entitySymbols.values(), getEntityString);\n }\n\n function registerComponent(component: Component) {\n components.push(component);\n }\n\n function dispose(namespace?: string) {\n for (const [, disposer] of disposers.filter((d) => !namespace || d[0] === namespace)) {\n disposer();\n }\n disposers = disposers.filter((d) => namespace && d[0] !== namespace);\n }\n\n function registerDisposer(disposer: () => void, namespace = \"\") {\n disposers.push([namespace, disposer]);\n }\n\n function hasEntity(entity: Entity): boolean {\n const entitySymbol = getEntitySymbol(entity);\n return entitySymbols.has(entitySymbol);\n }\n\n function deleteEntity(entity: Entity) {\n for (const component of components) {\n if (hasComponent(component, entity)) removeComponent(component, entity);\n }\n entitySymbols.delete(getEntitySymbol(entity));\n }\n\n return {\n registerEntity,\n components,\n registerComponent,\n dispose,\n registerDisposer,\n hasEntity,\n getEntities,\n entitySymbols,\n deleteEntity,\n } satisfies World;\n}\n\n/**\n * Create a new namespace from an existing World.\n * The `dispose` method of a namespaced World only calls disposers registered on this namespace.\n *\n * @param world World to create a new namespace for.\n * @param namespace String descriptor of the new namespace.\n * @returns World with a new namespace.\n */\nexport function namespaceWorld(world: ReturnType<typeof createWorld>, namespace: string) {\n return {\n ...world,\n registerDisposer: (disposer: () => void) => world.registerDisposer(disposer, namespace),\n dispose: () => world.dispose(namespace),\n };\n}\n\n/**\n * Get all components that have a value for the given entity.\n *\n * @dev Design decision: don't store a list of components for each entity but compute it dynamically when needed\n * because there are less components than entities and maintaining a list of components per entity is a large overhead.\n *\n * @param world World object the given entity is registered on.\n * @param entity {@link Entity} to get the list of components for.\n * @returns Array of components that have a value for the given entity.\n */\nexport function getEntityComponents(world: World, entity: Entity): Component[] {\n return world.components.filter((component) => hasComponent(component, entity));\n}\n"],"mappings":"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","names":["concat","EMPTY","from","filterNullish","observable","concat","concatMap","filter","from","map","merge","of","share","QueryFragmentType","isOptionalType","t","isArrayType","isNumberType","isEntityType","Has","component","Not","HasValue","value","NotValue","ProxyRead","depth","ProxyExpand","passesQueryFragment","entity","fragment","hasComponent","componentValueEquals","getComponentValue","isPositiveFragment","isNegativeFragment","isSettingFragment","isBreakingPassState","passes","passesQueryFragmentProxy","proxyRead","proxyEntity","i","getChildEntities","directChildEntities","getEntitiesWithValue","indirectChildEntities","childEntity","runQuery","fragments","initialSet","entities","proxyExpand","childEntities","getComponentEntities","defineQuery","options","matching","observable","initial$","from","toUpdateStream","containsProxy","v","internal$","merge","f","concatMap","update","newMatchingSet","updates","previouslyMatchingEntity","matchingEntity","of","map","filterNullish","concat","share","defineUpdateQuery","filter","e","defineEnterQuery","defineExitQuery","defineRxSystem","world","observable$","system","subscription","defineUpdateSystem","query","options","defineUpdateQuery","defineEnterSystem","defineEnterQuery","defineExitSystem","defineExitQuery","defineSystem","defineQuery","defineComponentSystem","component","initial$","from","getComponentEntities","toUpdateStream","EMPTY","concat","defineSyncSystem","value","entity","type","setComponent","removeComponent","transformIterator","createWorld","entitySymbols","components","disposers","registerEntity","id","idSuffix","entity","entitySymbol","getEntitySymbol","getEntities","transformIterator","getEntityString","registerComponent","component","dispose","namespace","disposer","d","registerDisposer","hasEntity","deleteEntity","hasComponent","removeComponent","namespaceWorld","world","getEntityComponents"]}
|
1
|
+
{"version":3,"sources":["../src/System.ts","../src/Query.ts","../src/types.ts","../src/World.ts"],"sourcesContent":["import { concat, EMPTY, from, Observable } from \"rxjs\";\nimport { getComponentEntities, removeComponent, setComponent } from \"./Component\";\nimport { UpdateType } from \"./constants\";\nimport { defineEnterQuery, defineExitQuery, defineQuery, defineUpdateQuery } from \"./Query\";\nimport { Component, ComponentUpdate, ComponentValue, Entity, QueryFragment, Schema, World } from \"./types\";\nimport { toUpdateStream } from \"./utils\";\n\n/**\n * Create a system that is called on every update of the given observable.\n *\n * @remarks\n * Advantage of using this function over directly subscribing to the RxJS observable is that the system is registered in the `world` and\n * disposed when the `world` is disposed (eg. during a hot reload in development).\n *\n * @param world {@link World} object this system should be registered in.\n * @param observable$ Observable to react to.\n * @param system System function to run on updates of the `observable$`. System function gets passed the update events from the `observable$`.\n */\nexport function defineRxSystem<T>(world: World, observable$: Observable<T>, system: (event: T) => void) {\n const subscription = observable$.subscribe(system);\n world.registerDisposer(() => subscription?.unsubscribe());\n}\n\n/**\n * Create a system that is called on every event of the given {@link defineUpdateQuery update query}.\n *\n * @param world {@link World} object this system should be registered in.\n * @param query Update query to react to.\n * @param system System function to run when the result of the given update query changes.\n * @param options Optional: {\n * runOnInit: if true, run this system for all entities matching the query when the system is created.\n * Else only run on updates after the system is created. Default true.\n * }\n */\nexport function defineUpdateSystem(\n world: World,\n query: QueryFragment[],\n system: (update: ComponentUpdate) => void,\n options: { runOnInit?: boolean } = { runOnInit: true },\n) {\n defineRxSystem(world, defineUpdateQuery(query, options), system);\n}\n\n/**\n * Create a system that is called on every event of the given {@link defineEnterQuery enter query}.\n *\n * @param world {@link World} object this system should be registered in.\n * @param query Enter query to react to.\n * @param system System function to run when the result of the given enter query changes.\n * @param options Optional: {\n * runOnInit: if true, run this system for all entities matching the query when the system is created.\n * Else only run on updates after the system is created. Default true.\n * }\n */\nexport function defineEnterSystem(\n world: World,\n query: QueryFragment[],\n system: (update: ComponentUpdate) => void,\n options: { runOnInit?: boolean } = { runOnInit: true },\n) {\n defineRxSystem(world, defineEnterQuery(query, options), system);\n}\n\n/**\n * Create a system that is called on every event of the given {@link defineExitQuery exit query}.\n *\n * @param world {@link World} object this system should be registered in.\n * @param query Exit query to react to.\n * @param system System function to run when the result of the given exit query changes.\n * @param options Optional: {\n * runOnInit: if true, run this system for all entities matching the query when the system is created.\n * Else only run on updates after the system is created. Default true.\n * }\n */\nexport function defineExitSystem(\n world: World,\n query: QueryFragment[],\n system: (update: ComponentUpdate) => void,\n options: { runOnInit?: boolean } = { runOnInit: true },\n) {\n defineRxSystem(world, defineExitQuery(query, options), system);\n}\n\n/**\n * Create a system that is called on every event of the given {@link defineQuery query}.\n *\n * @param world {@link World} object this system should be registered in.\n * @param query Query to react to.\n * @param system System function to run when the result of the given query changes.\n * @param options Optional: {\n * runOnInit: if true, run this system for all entities matching the query when the system is created.\n * Else only run on updates after the system is created. Default true.\n * }\n */\nexport function defineSystem(\n world: World,\n query: QueryFragment[],\n system: (update: ComponentUpdate & { type: UpdateType }) => void,\n options: { runOnInit?: boolean } = { runOnInit: true },\n) {\n defineRxSystem(world, defineQuery(query, options).update$, system);\n}\n\n/**\n * Create a system that is called every time the given component is updated.\n *\n * @param world {@link World} object this system should be registered in.\n * @param component Component to whose updates to react.\n * @param system System function to run when the given component is updated.\n * @param options Optional: {\n * runOnInit: if true, run this system for all entities in the component when the system is created.\n * Else only run on updates after the system is created. Default true.\n * }\n */\nexport function defineComponentSystem<S extends Schema>(\n world: World,\n component: Component<S>,\n system: (update: ComponentUpdate<S>) => void,\n options: { runOnInit?: boolean } = { runOnInit: true },\n) {\n const initial$ = options?.runOnInit ? from(getComponentEntities(component)).pipe(toUpdateStream(component)) : EMPTY;\n defineRxSystem(world, concat(initial$, component.update$), system);\n}\n\n/**\n * Create a system to synchronize updates to one component with another component.\n *\n * @param world {@link World} object this system should be registered in.\n * @param query Result of `component` is added to all entites matching this query.\n * @param component Function returning the component to be added to all entities matching the given query.\n * @param value Function returning the component value to be added to all entities matching the given query.\n */\nexport function defineSyncSystem<T extends Schema>(\n world: World,\n query: QueryFragment[],\n component: (entity: Entity) => Component<T>,\n value: (entity: Entity) => ComponentValue<T>,\n options: { update?: boolean; runOnInit?: boolean } = { update: false, runOnInit: true },\n) {\n defineSystem(\n world,\n query,\n ({ entity, type }) => {\n if (type === UpdateType.Enter) setComponent(component(entity), entity, value(entity));\n if (type === UpdateType.Exit) removeComponent(component(entity), entity);\n if (options?.update && type === UpdateType.Update) setComponent(component(entity), entity, value(entity));\n },\n options,\n );\n}\n","import { filterNullish } from \"@latticexyz/utils\";\nimport { observable, ObservableSet } from \"mobx\";\nimport { concat, concatMap, filter, from, map, merge, Observable, of, share } from \"rxjs\";\nimport {\n componentValueEquals,\n getComponentEntities,\n getComponentValue,\n getEntitiesWithValue,\n hasComponent,\n} from \"./Component\";\nimport { UpdateType, Type } from \"./constants\";\nimport {\n Component,\n ComponentUpdate,\n ComponentValue,\n Entity,\n EntityQueryFragment,\n HasQueryFragment,\n HasValueQueryFragment,\n NotQueryFragment,\n NotValueQueryFragment,\n ProxyExpandQueryFragment,\n ProxyReadQueryFragment,\n QueryFragment,\n QueryFragmentType,\n Schema,\n SettingQueryFragment,\n} from \"./types\";\nimport { toUpdateStream } from \"./utils\";\n\n/**\n * Create a {@link HasQueryFragment}.\n *\n * @remarks\n * The {@link HasQueryFragment} filters for entities that have the given component,\n * independent from the component value.\n *\n * @example\n * Query for all entities with a `Position`.\n * ```\n * runQuery([Has(Position)]);\n * ```\n *\n * @param component Component this query fragment refers to.\n * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.\n */\nexport function Has<T extends Schema>(component: Component<T>): HasQueryFragment<T> {\n return { type: QueryFragmentType.Has, component };\n}\n\n/**\n * Create a {@link NotQueryFragment}.\n *\n * @remarks\n * The {@link NotQueryFragment} filters for entities that don't have the given component,\n * independent from the component value.\n *\n * @example\n * Query for all entities with a `Position` that are not `Movable`.\n * ```\n * runQuery([Has(Position), Not(Movable)]);\n * ```\n *\n * @param component Component this query fragment refers to.\n * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.\n */\nexport function Not<T extends Schema>(component: Component<T>): NotQueryFragment<T> {\n return { type: QueryFragmentType.Not, component };\n}\n\n/**\n * Create a {@link HasValueQueryFragment}.\n *\n * @remarks\n * The {@link HasValueQueryFragment} filters for entities that have the given component\n * with the given component value.\n *\n * @example\n * Query for all entities at Position (0,0).\n * ```\n * runQuery([HasValue(Position, { x: 0, y: 0 })]);\n * ```\n *\n * @param component Component this query fragment refers to.\n * @param value Only include entities with this (partial) component value from the result.\n * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.\n */\nexport function HasValue<T extends Schema>(\n component: Component<T>,\n value: Partial<ComponentValue<T>>,\n): HasValueQueryFragment<T> {\n return { type: QueryFragmentType.HasValue, component, value };\n}\n\n/**\n * Create a {@link NotValueQueryFragment}.\n *\n * @remarks\n * The {@link NotValueQueryFragment} filters for entities that don't have the given component\n * with the given component value.\n *\n * @example\n * Query for all entities that have a `Position`, except for those at `Position` (0,0).\n * ```\n * runQuery([Has(Position), NotValue(Position, { x: 0, y: 0 })]);\n * ```\n *\n * @param component Component this query fragment refers to.\n * @param value Exclude entities with this (partial) component value from the result.\n * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.\n */\nexport function NotValue<T extends Schema>(\n component: Component<T>,\n value: Partial<ComponentValue<T>>,\n): NotValueQueryFragment<T> {\n return { type: QueryFragmentType.NotValue, component, value };\n}\n\n/**\n * Create a {@link ProxyReadQueryFragment}.\n *\n * @remarks\n * The {@link ProxyReadQueryFragment} activates the \"proxy read mode\" for the rest of the query.\n * This means that for all remaining fragments in the query not only the entities themselves are checked, but also\n * their \"ancestors\" up to the given `depth` on the relationship chain defined by the given `component`.\n *\n * @example\n * Query for all entities that have a `Position` and are (directly or indirectly) owned by an entity with `Name` \"Alice\".\n * ```\n * runQuery([Has(Position), ProxyRead(OwnedByEntity, Number.MAX_SAFE_INTEGER), HasValue(Name, { name: \"Alice\" })]);\n * ```\n *\n * @param component Component this query fragment refers to.\n * @param depth Max depth in the relationship chain to traverse.\n * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.\n */\nexport function ProxyRead(component: Component<{ value: Type.Entity }>, depth: number): ProxyReadQueryFragment {\n return { type: QueryFragmentType.ProxyRead, component, depth };\n}\n\n/**\n * Create a {@link ProxyExpandQueryFragment}.\n *\n * @remarks\n * The {@link ProxyExpandQueryFragment} activates the \"proxy expand mode\" for the rest of the query.\n * This means that for all remaining fragments in the query not only the matching entities themselves are included in the intermediate set,\n * but also all their \"children\" down to the given `depth` on the relationship chain defined by the given `component`.\n *\n * @example\n * Query for all entities (directly or indirectly) owned by an entity with `Name` \"Alice\".\n * ```\n * runQuery([ProxyExpand(OwnedByEntity, Number.MAX_SAFE_INTEGER), HasValue(Name, { name: \"Alice\" })]);\n * ```\n *\n * @param component Component to apply this query fragment to.\n * @param depth Max depth in the relationship chain to traverse.\n * @returns query fragment to be used in {@link runQuery} or {@link defineQuery}.\n */\nexport function ProxyExpand(component: Component<{ value: Type.Entity }>, depth: number): ProxyExpandQueryFragment {\n return { type: QueryFragmentType.ProxyExpand, component, depth };\n}\n\n/**\n * Helper function to check whether a given entity passes a given query fragment.\n *\n * @param entity Entity to check.\n * @param fragment Query fragment to check.\n * @returns True if the entity passes the query fragment, else false.\n */\nfunction passesQueryFragment<T extends Schema>(entity: Entity, fragment: EntityQueryFragment<T>): boolean {\n if (fragment.type === QueryFragmentType.Has) {\n // Entity must have the given component\n return hasComponent(fragment.component, entity);\n }\n\n if (fragment.type === QueryFragmentType.HasValue) {\n // Entity must have the given component value\n return componentValueEquals(fragment.value, getComponentValue(fragment.component, entity));\n }\n\n if (fragment.type === QueryFragmentType.Not) {\n // Entity must not have the given component\n return !hasComponent(fragment.component, entity);\n }\n\n if (fragment.type === QueryFragmentType.NotValue) {\n // Entity must not have the given component value\n return !componentValueEquals(fragment.value, getComponentValue(fragment.component, entity));\n }\n\n throw new Error(\"Unknown query fragment\");\n}\n\n/**\n * Helper function to check whether a query fragment is \"positive\" (ie `Has` or `HasValue`)\n *\n * @param fragment Query fragment to check.\n * @returns True if the query fragment is positive, else false.\n */\nfunction isPositiveFragment<T extends Schema>(\n fragment: QueryFragment<T>,\n): fragment is HasQueryFragment<T> | HasValueQueryFragment<T> {\n return fragment.type === QueryFragmentType.Has || fragment.type == QueryFragmentType.HasValue;\n}\n\n/**\n * Helper function to check whether a query fragment is \"negative\" (ie `Not` or `NotValue`)\n *\n * @param fragment Query fragment to check.\n * @returns True if the query fragment is negative, else false.\n */\nfunction isNegativeFragment<T extends Schema>(\n fragment: QueryFragment<T>,\n): fragment is NotQueryFragment<T> | NotValueQueryFragment<T> {\n return fragment.type === QueryFragmentType.Not || fragment.type == QueryFragmentType.NotValue;\n}\n\n/**\n * Helper function to check whether a query fragment is a setting fragment (ie `ProxyExpand` or `ProxyRead`)\n *\n * @param fragment Query fragment to check.\n * @returns True if the query fragment is a setting fragment, else false.\n */\nfunction isSettingFragment<T extends Schema>(fragment: QueryFragment<T>): fragment is SettingQueryFragment {\n return fragment.type === QueryFragmentType.ProxyExpand || fragment.type == QueryFragmentType.ProxyRead;\n}\n\n/**\n * Helper function to check whether the result of a query pass check is a breaking state.\n *\n * @remarks\n * For positive fragments (Has/HasValue) we need to find any passing entity up the proxy chain\n * so as soon as passes is true, we can early return. For negative fragments (Not/NotValue) every entity\n * up the proxy chain must pass, so we can early return if we find one that doesn't pass.\n *\n * @param passes Boolean result of previous query pass check.\n * @param fragment Fragment that was used in the query pass check.\n * @returns True if the result is breaking pass state, else false.\n */\nfunction isBreakingPassState(passes: boolean, fragment: EntityQueryFragment<Schema>) {\n return (passes && isPositiveFragment(fragment)) || (!passes && isNegativeFragment(fragment));\n}\n\n/**\n * Helper function to check whether an entity passes a query fragment when taking into account a {@link ProxyReadQueryFragment}.\n *\n * @param entity {@link Entity} of the entity to check.\n * @param fragment Query fragment to check.\n * @param proxyRead {@link ProxyReadQueryFragment} to take into account.\n * @returns True if the entity passes the query fragment, else false.\n */\nfunction passesQueryFragmentProxy<T extends Schema>(\n entity: Entity,\n fragment: EntityQueryFragment<T>,\n proxyRead: ProxyReadQueryFragment,\n): boolean | null {\n let proxyEntity = entity;\n let passes = false;\n for (let i = 0; i < proxyRead.depth; i++) {\n const value = getComponentValue(proxyRead.component, proxyEntity);\n // If the current entity does not have the proxy component, abort\n if (!value) return null;\n\n const entity = value.value;\n if (!entity) return null;\n\n // Move up the proxy chain\n proxyEntity = entity;\n passes = passesQueryFragment(proxyEntity, fragment);\n\n if (isBreakingPassState(passes, fragment)) {\n return passes;\n }\n }\n return passes;\n}\n\n/**\n * Recursively compute all direct and indirect child entities up to the specified depth\n * down the relationship chain defined by the given component.\n *\n * @param entity Entity to get all child entities for up to the specified depth\n * @param component Component to use for the relationship chain.\n * @param depth Depth up to which the recursion should be applied.\n * @returns Set of entities that are child entities of the given entity via the given component.\n */\nexport function getChildEntities(\n entity: Entity,\n component: Component<{ value: Type.Entity }>,\n depth: number,\n): Set<Entity> {\n if (depth === 0) return new Set();\n\n const directChildEntities = getEntitiesWithValue(component, { value: entity });\n if (depth === 1) return directChildEntities;\n\n const indirectChildEntities = [...directChildEntities]\n .map((childEntity) => [...getChildEntities(childEntity, component, depth - 1)])\n .flat();\n\n return new Set([...directChildEntities, ...indirectChildEntities]);\n}\n\n/**\n * Execute a list of query fragments to receive a Set of matching entities.\n *\n * @remarks\n * The query fragments are executed from left to right and are concatenated with a logical `AND`.\n * For performance reasons, the most restrictive query fragment should be first in the list of query fragments,\n * in order to reduce the number of entities the next query fragment needs to be checked for.\n * If no proxy fragments are used, every entity in the resulting set passes every query fragment.\n * If setting fragments are used, the order of the query fragments influences the result, since settings only apply to\n * fragments after the setting fragment.\n *\n * @param fragments Query fragments to execute.\n * @param initialSet Optional: provide a Set of entities to execute the query on. If none is given, all existing entities are used for the query.\n * @returns Set of entities matching the query fragments.\n */\nexport function runQuery(fragments: QueryFragment[], initialSet?: Set<Entity>): Set<Entity> {\n let entities: Set<Entity> | undefined = initialSet ? new Set([...initialSet]) : undefined; // Copy to a fresh set because it will be modified in place\n let proxyRead: ProxyReadQueryFragment | undefined = undefined;\n let proxyExpand: ProxyExpandQueryFragment | undefined = undefined;\n\n // Process fragments\n for (let i = 0; i < fragments.length; i++) {\n const fragment = fragments[i];\n if (isSettingFragment(fragment)) {\n // Store setting fragments for subsequent query fragments\n if (fragment.type === QueryFragmentType.ProxyRead) proxyRead = fragment;\n if (fragment.type === QueryFragmentType.ProxyExpand) proxyExpand = fragment;\n } else if (!entities) {\n // Handle entity query fragments\n // First regular fragment must be Has or HasValue\n if (isNegativeFragment(fragment)) {\n throw new Error(\"First EntityQueryFragment must be Has or HasValue\");\n }\n\n // Create the first interim result\n entities =\n fragment.type === QueryFragmentType.Has\n ? new Set([...getComponentEntities(fragment.component)])\n : getEntitiesWithValue(fragment.component, fragment.value);\n\n // Add entity's children up to the specified depth if proxy expand is active\n if (proxyExpand && proxyExpand.depth > 0) {\n for (const entity of [...entities]) {\n for (const childEntity of getChildEntities(entity, proxyExpand.component, proxyExpand.depth)) {\n entities.add(childEntity);\n }\n }\n }\n } else {\n // There already is an interim result, apply the current fragment\n for (const entity of [...entities]) {\n // Branch 1: Simple / check if the current entity passes the query fragment\n let passes = passesQueryFragment(entity, fragment);\n\n // Branch 2: Proxy upwards / check if proxy entity passes the query\n if (proxyRead && proxyRead.depth > 0 && !isBreakingPassState(passes, fragment)) {\n passes = passesQueryFragmentProxy(entity, fragment, proxyRead) ?? passes;\n }\n\n // If the entity didn't pass the query fragment, remove it from the interim set\n if (!passes) entities.delete(entity);\n\n // Branch 3: Proxy downwards / run the query fragments on child entities if proxy expand is active\n if (proxyExpand && proxyExpand.depth > 0) {\n const childEntities = getChildEntities(entity, proxyExpand.component, proxyExpand.depth);\n for (const childEntity of childEntities) {\n // Add the child entity if it passes the direct check\n // or if a proxy read is active and it passes the proxy read check\n if (\n passesQueryFragment(childEntity, fragment) ||\n (proxyRead && proxyRead.depth > 0 && passesQueryFragmentProxy(childEntity, fragment, proxyRead))\n )\n entities.add(childEntity);\n }\n }\n }\n }\n }\n\n return entities ?? new Set<Entity>();\n}\n\n/**\n * Create a query object including an update$ stream and a Set of entities currently matching the query.\n *\n * @remarks\n * `update$` stream needs to be subscribed to in order for the logic inside the stream to be executed and therefore\n * in order for the `matching` set to be updated.\n *\n * `defineQuery` should be strongly preferred over `runQuery` if the query is used for systems or other\n * use cases that repeatedly require the query result or updates to the query result. `defineQuery` does not\n * reevaluate the entire query if an accessed component changes, but only performs the minimal set of checks\n * on the updated entity to evaluate wether the entity still matches the query, resulting in significant performance\n * advantages over `runQuery`.\n *\n * The query fragments are executed from left to right and are concatenated with a logical `AND`.\n * For performance reasons, the most restrictive query fragment should be first in the list of query fragments,\n * in order to reduce the number of entities the next query fragment needs to be checked for.\n * If no proxy fragments are used, every entity in the resulting set passes every query fragment.\n * If setting fragments are used, the order of the query fragments influences the result, since settings only apply to\n * fragments after the setting fragment.\n *\n * @param fragments Query fragments to execute.\n * @param options Optional: {\n * runOnInit: if true, the query is executed once with `runQuery` to build an iniital Set of matching entities. If false only updates after the query was created are considered.\n * initialSet: if given, this set is passed to `runOnInit` when building the initial Set of matching entities.\n * }\n * @returns Query object: {\n * update$: RxJS stream of updates to the query result. The update contains the component update that caused the query update, as well as the {@link UpdateType update type}.\n * matching: Mobx observable set of entities currently matching the query.\n * }.\n */\nexport function defineQuery(\n fragments: QueryFragment[],\n options?: { runOnInit?: boolean; initialSet?: Set<Entity> },\n): {\n update$: Observable<ComponentUpdate & { type: UpdateType }>;\n matching: ObservableSet<Entity>;\n} {\n const initialSet =\n options?.runOnInit || options?.initialSet ? runQuery(fragments, options.initialSet) : new Set<Entity>();\n\n const matching = observable(initialSet);\n const initial$ = from(matching).pipe(toUpdateStream(fragments[0].component));\n\n const containsProxy =\n fragments.findIndex((v) => [QueryFragmentType.ProxyExpand, QueryFragmentType.ProxyRead].includes(v.type)) !== -1;\n\n const internal$ = merge(...fragments.map((f) => f.component.update$)) // Combine all component update streams accessed accessed in this query\n .pipe(\n containsProxy // Query contains proxies\n ? concatMap((update) => {\n // If the query contains proxy read or expand fragments, entities up or down the proxy chain might match due to this update.\n // We have to run the entire query again and compare the result.\n // TODO: We might be able to make this more efficient by first computing the set of entities that are potentially touched by this update\n // and then only rerun the query on this set.\n const newMatchingSet = runQuery(fragments, options?.initialSet);\n const updates: (ComponentUpdate & { type: UpdateType })[] = [];\n\n for (const previouslyMatchingEntity of matching) {\n // Entity matched before but doesn't match now\n if (!newMatchingSet.has(previouslyMatchingEntity)) {\n matching.delete(previouslyMatchingEntity);\n updates.push({\n entity: previouslyMatchingEntity,\n type: UpdateType.Exit,\n component: update.component,\n value: [undefined, undefined],\n });\n }\n }\n\n for (const matchingEntity of newMatchingSet) {\n if (matching.has(matchingEntity)) {\n // Entity matched before and still matches\n updates.push({\n entity: matchingEntity,\n type: UpdateType.Update,\n component: update.component,\n value: [getComponentValue(update.component, matchingEntity), undefined],\n });\n } else {\n // Entity didn't match before but matches now\n matching.add(matchingEntity);\n updates.push({\n entity: matchingEntity,\n type: UpdateType.Enter,\n component: update.component,\n value: [getComponentValue(update.component, matchingEntity), undefined],\n });\n }\n }\n\n return of(...updates);\n })\n : // Query does not contain proxies\n map((update) => {\n if (matching.has(update.entity)) {\n // If this entity matched the query before, check if it still matches it\n // Find fragments accessign this component (linear search is fine since the number fragments is likely small)\n const relevantFragments = fragments.filter((f) => f.component.id === update.component.id);\n const pass = relevantFragments.every((f) => passesQueryFragment(update.entity, f as EntityQueryFragment)); // We early return if the query contains proxies\n\n if (pass) {\n // Entity passed before and still passes, forward update\n return { ...update, type: UpdateType.Update };\n } else {\n // Entity passed before but not anymore, forward update and exit\n matching.delete(update.entity);\n return { ...update, type: UpdateType.Exit };\n }\n }\n\n // This entity didn't match before, check all fragments\n const pass = fragments.every((f) => passesQueryFragment(update.entity, f as EntityQueryFragment)); // We early return if the query contains proxies\n if (pass) {\n // Entity didn't pass before but passes now, forward update end enter\n matching.add(update.entity);\n return { ...update, type: UpdateType.Enter };\n }\n }),\n filterNullish(),\n );\n\n return {\n matching,\n update$: concat(initial$, internal$).pipe(share()),\n };\n}\n\n/**\n * Define a query object that only passes update events of type {@link UpdateType}.Update to the `update$` stream.\n * See {@link defineQuery} for details.\n *\n * @param fragments Query fragments\n * @returns Stream of component updates of entities that had already matched the query\n */\nexport function defineUpdateQuery(\n fragments: QueryFragment[],\n options?: { runOnInit?: boolean },\n): Observable<ComponentUpdate & { type: UpdateType }> {\n return defineQuery(fragments, options).update$.pipe(filter((e) => e.type === UpdateType.Update));\n}\n\n/**\n * Define a query object that only passes update events of type {@link UpdateType}.Enter to the `update$` stream.\n * See {@link defineQuery} for details.\n *\n * @param fragments Query fragments\n * @returns Stream of component updates of entities matching the query for the first time\n */\nexport function defineEnterQuery(\n fragments: QueryFragment[],\n options?: { runOnInit?: boolean },\n): Observable<ComponentUpdate> {\n return defineQuery(fragments, options).update$.pipe(filter((e) => e.type === UpdateType.Enter));\n}\n\n/**\n * Define a query object that only passes update events of type {@link UpdateType}.Exit to the `update$` stream.\n * See {@link defineQuery} for details.\n *\n * @param fragments Query fragments\n * @returns Stream of component updates of entities not matching the query anymore for the first time\n */\nexport function defineExitQuery(\n fragments: QueryFragment[],\n options?: { runOnInit?: boolean },\n): Observable<ComponentUpdate> {\n return defineQuery(fragments, options).update$.pipe(filter((e) => e.type === UpdateType.Exit));\n}\n","import { Subject } from \"rxjs\";\nimport { Type } from \"./constants\";\nimport { Opaque } from \"type-fest\";\n\n/**\n * Entities are represented as symbols internally for memory efficiency.\n * To get the entity's string representation, use `getEntityString(entitySymbol)`\n */\nexport type EntitySymbol = Opaque<symbol, \"EntitySymbol\">;\n\nexport type Entity = Opaque<string, \"Entity\">;\n\n/**\n * Used to define the schema of a {@link Component}.\n * Uses {@link Type} enum to be able to access the component type in JavaScript as well as have TypeScript type checks.\n */\nexport type Schema = {\n [key: string]: Type;\n};\n\n/**\n * Used to add arbitrary metadata to components.\n */\nexport type Metadata =\n | {\n [key: string]: unknown;\n }\n | undefined;\n\n/**\n * Mapping between JavaScript {@link Type} enum and corresponding TypeScript type.\n */\nexport type ValueType<T = unknown> = {\n [Type.Boolean]: boolean;\n [Type.Number]: number;\n [Type.BigInt]: bigint;\n [Type.String]: string;\n [Type.NumberArray]: number[];\n [Type.BigIntArray]: bigint[];\n [Type.StringArray]: string[];\n [Type.Entity]: Entity;\n [Type.EntityArray]: Entity[];\n [Type.OptionalNumber]: number | undefined;\n [Type.OptionalBigInt]: bigint | undefined;\n [Type.OptionalBigIntArray]: bigint[] | undefined;\n [Type.OptionalString]: string | undefined;\n [Type.OptionalNumberArray]: number[] | undefined;\n [Type.OptionalStringArray]: string[] | undefined;\n [Type.OptionalEntity]: Entity | undefined;\n [Type.OptionalEntityArray]: Entity[] | undefined;\n [Type.T]: T;\n [Type.OptionalT]: T | undefined;\n};\n\n/**\n * Used to infer the TypeScript type of a component value corresponding to a given {@link Schema}.\n */\nexport type ComponentValue<S extends Schema = Schema, T = unknown> = {\n [key in keyof S]: ValueType<T>[S[key]];\n};\n\n/**\n * Type of a component update corresponding to a given {@link Schema}.\n */\nexport type ComponentUpdate<S extends Schema = Schema, T = unknown> = {\n entity: Entity;\n value: [ComponentValue<S, T> | undefined, ComponentValue<S, T> | undefined];\n component: Component<S, Metadata, T>;\n};\n\n/**\n * Type of component returned by {@link defineComponent}.\n */\nexport interface Component<S extends Schema = Schema, M extends Metadata = Metadata, T = unknown> {\n id: string;\n values: { [key in keyof S]: Map<EntitySymbol, ValueType<T>[S[key]]> };\n schema: S;\n metadata: M;\n entities: () => IterableIterator<Entity>;\n world: World;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n update$: Subject<ComponentUpdate<S, T>> & { observers: any };\n}\n\n/**\n * Type of indexer returned by {@link createIndexer}.\n */\nexport type Indexer<S extends Schema, M extends Metadata = Metadata, T = unknown> = Component<S, M, T> & {\n getEntitiesWithValue: (value: ComponentValue<S, T>) => Set<Entity>;\n};\n\nexport type Components = {\n [key: string]: Component;\n};\n\nexport interface ComponentWithStream<S extends Schema, T = unknown> extends Component<S, Metadata, T> {\n stream$: Subject<{ entity: Entity; value: ComponentValue<S, T> | undefined }>;\n}\n\nexport type AnyComponentValue = ComponentValue<Schema>;\n\nexport type AnyComponent = Component<Schema>;\n\n/**\n * Type of World returned by {@link createWorld}.\n */\nexport type World = {\n registerEntity: (options?: { id?: string; idSuffix?: string }) => Entity;\n registerComponent: (component: Component) => void;\n components: Component[];\n getEntities: () => IterableIterator<Entity>;\n dispose: () => void;\n registerDisposer: (disposer: () => void) => void;\n hasEntity: (entity: Entity) => boolean;\n deleteEntity: (entity: Entity) => void;\n entitySymbols: Set<EntitySymbol>;\n};\n\nexport enum QueryFragmentType {\n Has,\n HasValue,\n Not,\n NotValue,\n ProxyRead,\n ProxyExpand,\n}\n\nexport type HasQueryFragment<T extends Schema> = {\n type: QueryFragmentType.Has;\n component: Component<T>;\n};\n\nexport type HasValueQueryFragment<T extends Schema> = {\n type: QueryFragmentType.HasValue;\n component: Component<T>;\n value: Partial<ComponentValue<T>>;\n};\n\nexport type NotQueryFragment<T extends Schema> = {\n type: QueryFragmentType.Not;\n component: Component<T>;\n};\n\nexport type NotValueQueryFragment<T extends Schema> = {\n type: QueryFragmentType.NotValue;\n component: Component<T>;\n value: Partial<ComponentValue<T>>;\n};\n\nexport type ProxyReadQueryFragment = {\n type: QueryFragmentType.ProxyRead;\n component: Component<{ value: Type.Entity }>;\n depth: number;\n};\n\nexport type ProxyExpandQueryFragment = {\n type: QueryFragmentType.ProxyExpand;\n component: Component<{ value: Type.Entity }>;\n depth: number;\n};\n\nexport type QueryFragment<T extends Schema = Schema> =\n | HasQueryFragment<T>\n | HasValueQueryFragment<T>\n | NotQueryFragment<T>\n | NotValueQueryFragment<T>\n | ProxyReadQueryFragment\n | ProxyExpandQueryFragment;\n\nexport type EntityQueryFragment<T extends Schema = Schema> =\n | HasQueryFragment<T>\n | HasValueQueryFragment<T>\n | NotQueryFragment<T>\n | NotValueQueryFragment<T>;\n\nexport type SettingQueryFragment = ProxyReadQueryFragment | ProxyExpandQueryFragment;\n\nexport type QueryFragments = QueryFragment<Schema>[];\n\nexport type SchemaOf<C extends Component<Schema>> = C extends Component<infer S> ? S : never;\n\nexport type Override<S extends Schema, T = unknown> = {\n entity: Entity;\n value: Partial<ComponentValue<S, T>> | null;\n};\n\n/**\n * Type of overridable component returned by {@link overridableComponent}.\n */\nexport type OverridableComponent<S extends Schema = Schema, M extends Metadata = Metadata, T = unknown> = Component<\n S,\n M,\n T\n> & {\n addOverride: (overrideId: string, update: Override<S, T>) => void;\n removeOverride: (overrideId: string) => void;\n};\n\nexport type OptionalType =\n | Type.OptionalNumber\n | Type.OptionalBigInt\n | Type.OptionalString\n | Type.OptionalEntity\n | Type.OptionalNumberArray\n | Type.OptionalBigIntArray\n | Type.OptionalStringArray\n | Type.OptionalEntityArray;\n\nexport function isOptionalType(t: Type): t is OptionalType {\n return [\n Type.OptionalNumber,\n Type.OptionalBigInt,\n Type.OptionalString,\n Type.OptionalEntity,\n Type.OptionalEntityArray,\n Type.OptionalNumberArray,\n Type.OptionalBigIntArray,\n Type.OptionalStringArray,\n ].includes(t);\n}\n\nexport type ArrayType =\n | Type.NumberArray\n | Type.OptionalNumberArray\n | Type.BigIntArray\n | Type.OptionalBigIntArray\n | Type.StringArray\n | Type.OptionalStringArray\n | Type.EntityArray\n | Type.OptionalEntityArray;\n\nexport function isArrayType(t: Type): t is ArrayType {\n return [\n Type.NumberArray,\n Type.OptionalNumberArray,\n Type.BigIntArray,\n Type.OptionalBigIntArray,\n Type.StringArray,\n Type.OptionalStringArray,\n Type.EntityArray,\n Type.OptionalEntityArray,\n ].includes(t);\n}\n\nexport type NumberType = Type.Number | Type.OptionalNumber;\nexport function isNumberType(t: Type): t is NumberType {\n return [Type.Number, Type.OptionalNumber].includes(t);\n}\n\nexport type EntityType = Type.Entity | Type.OptionalEntity;\nexport function isEntityType(t: Type): t is EntityType {\n return [Type.Entity, Type.OptionalEntity].includes(t);\n}\n\nexport type Layer = {\n world: World;\n components: Record<string, Component<Schema>>;\n};\n\nexport type Layers = Record<string, Layer>;\n","import { transformIterator } from \"@latticexyz/utils\";\nimport { hasComponent, removeComponent } from \"./Component\";\nimport { getEntityString, getEntitySymbol } from \"./Entity\";\nimport { Component, Entity, EntitySymbol, World } from \"./types\";\n\n/**\n * Create a new World.\n *\n * @remarks\n * A World is the central object of an ECS application, where all {@link defineComponent Components},\n * {@link registerEntity Entities} and {@link defineSystem Systems} are registerd.\n *\n * @returns A new World\n */\nexport function createWorld() {\n const entitySymbols = new Set<EntitySymbol>();\n const components: Component[] = [];\n let disposers: [string, () => void][] = [];\n\n function registerEntity({ id, idSuffix }: { id?: string; idSuffix?: string } = {}) {\n const entity = (id || entitySymbols.size + (idSuffix ? \"-\" + idSuffix : \"\")) as Entity;\n const entitySymbol = getEntitySymbol(entity);\n\n // Register entity\n entitySymbols.add(entitySymbol);\n\n return entity;\n }\n\n function getEntities() {\n return transformIterator(entitySymbols.values(), getEntityString);\n }\n\n function registerComponent(component: Component) {\n components.push(component);\n }\n\n function dispose(namespace?: string) {\n for (const [, disposer] of disposers.filter((d) => !namespace || d[0] === namespace)) {\n disposer();\n }\n disposers = disposers.filter((d) => namespace && d[0] !== namespace);\n }\n\n function registerDisposer(disposer: () => void, namespace = \"\") {\n disposers.push([namespace, disposer]);\n }\n\n function hasEntity(entity: Entity): boolean {\n const entitySymbol = getEntitySymbol(entity);\n return entitySymbols.has(entitySymbol);\n }\n\n function deleteEntity(entity: Entity) {\n for (const component of components) {\n if (hasComponent(component, entity)) removeComponent(component, entity);\n }\n entitySymbols.delete(getEntitySymbol(entity));\n }\n\n return {\n registerEntity,\n components,\n registerComponent,\n dispose,\n registerDisposer,\n hasEntity,\n getEntities,\n entitySymbols,\n deleteEntity,\n } satisfies World;\n}\n\n/**\n * Create a new namespace from an existing World.\n * The `dispose` method of a namespaced World only calls disposers registered on this namespace.\n *\n * @param world World to create a new namespace for.\n * @param namespace String descriptor of the new namespace.\n * @returns World with a new namespace.\n */\nexport function namespaceWorld(world: ReturnType<typeof createWorld>, namespace: string) {\n return {\n ...world,\n registerDisposer: (disposer: () => void) => world.registerDisposer(disposer, namespace),\n dispose: () => world.dispose(namespace),\n };\n}\n\n/**\n * Get all components that have a value for the given entity.\n *\n * @dev Design decision: don't store a list of components for each entity but compute it dynamically when needed\n * because there are less components than entities and maintaining a list of components per entity is a large overhead.\n *\n * @param world World object the given entity is registered on.\n * @param entity {@link Entity} to get the list of components for.\n * @returns Array of components that have a value for the given entity.\n */\nexport function getEntityComponents(world: World, entity: Entity): Component[] {\n return world.components.filter((component) => hasComponent(component, entity));\n}\n"],"mappings":"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","names":["concat","EMPTY","from","filterNullish","observable","concat","concatMap","filter","from","map","merge","of","share","QueryFragmentType","isOptionalType","t","isArrayType","isNumberType","isEntityType","Has","component","Not","HasValue","value","NotValue","ProxyRead","depth","ProxyExpand","passesQueryFragment","entity","fragment","hasComponent","componentValueEquals","getComponentValue","isPositiveFragment","isNegativeFragment","isSettingFragment","isBreakingPassState","passes","passesQueryFragmentProxy","proxyRead","proxyEntity","i","getChildEntities","directChildEntities","getEntitiesWithValue","indirectChildEntities","childEntity","runQuery","fragments","initialSet","entities","proxyExpand","childEntities","getComponentEntities","defineQuery","options","matching","observable","initial$","from","toUpdateStream","containsProxy","v","internal$","merge","f","concatMap","update","newMatchingSet","updates","previouslyMatchingEntity","matchingEntity","of","map","filterNullish","concat","share","defineUpdateQuery","filter","e","defineEnterQuery","defineExitQuery","defineRxSystem","world","observable$","system","subscription","defineUpdateSystem","query","options","defineUpdateQuery","defineEnterSystem","defineEnterQuery","defineExitSystem","defineExitQuery","defineSystem","defineQuery","defineComponentSystem","component","initial$","from","getComponentEntities","toUpdateStream","EMPTY","concat","defineSyncSystem","value","entity","type","setComponent","removeComponent","transformIterator","createWorld","entitySymbols","components","disposers","registerEntity","id","idSuffix","entity","entitySymbol","getEntitySymbol","getEntities","transformIterator","getEntityString","registerComponent","component","dispose","namespace","disposer","d","registerDisposer","hasEntity","deleteEntity","hasComponent","removeComponent","namespaceWorld","world","getEntityComponents"]}
|
@@ -221,4 +221,4 @@ type Layer = {
|
|
221
221
|
};
|
222
222
|
type Layers = Record<string, Layer>;
|
223
223
|
|
224
|
-
export { AnyComponentValue as A, Component as C, Entity as E, HasQueryFragment as H, Indexer as I, Layer as L, Metadata as M, NotQueryFragment as N, OverridableComponent as O, ProxyReadQueryFragment as P, QueryFragment as Q, Schema as S, Type as T, UpdateType as U, ValueType as V, World as W, ComponentValue as a, EntitySymbol as b, ComponentUpdate as c, HasValueQueryFragment as d, NotValueQueryFragment as e, ProxyExpandQueryFragment as f, Components as g, ComponentWithStream as h, AnyComponent as i, QueryFragmentType as j, EntityQueryFragment as k, SettingQueryFragment as l, QueryFragments as m, SchemaOf as n, Override as o, OptionalType as p, isOptionalType as q, ArrayType as r, isArrayType as s, NumberType as t, isNumberType as u, EntityType as v, isEntityType as w, Layers as x, OptionalTypes as y };
|
224
|
+
export { type AnyComponentValue as A, type Component as C, type Entity as E, type HasQueryFragment as H, type Indexer as I, type Layer as L, type Metadata as M, type NotQueryFragment as N, type OverridableComponent as O, type ProxyReadQueryFragment as P, type QueryFragment as Q, type Schema as S, Type as T, UpdateType as U, type ValueType as V, type World as W, type ComponentValue as a, type EntitySymbol as b, type ComponentUpdate as c, type HasValueQueryFragment as d, type NotValueQueryFragment as e, type ProxyExpandQueryFragment as f, type Components as g, type ComponentWithStream as h, type AnyComponent as i, QueryFragmentType as j, type EntityQueryFragment as k, type SettingQueryFragment as l, type QueryFragments as m, type SchemaOf as n, type Override as o, type OptionalType as p, isOptionalType as q, type ArrayType as r, isArrayType as s, type NumberType as t, isNumberType as u, type EntityType as v, isEntityType as w, type Layers as x, OptionalTypes as y };
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@latticexyz/recs",
|
3
|
-
"version": "2.2.
|
3
|
+
"version": "2.2.16-59389b1e37bc84664972231989ce7fdc739cce42",
|
4
4
|
"repository": {
|
5
5
|
"type": "git",
|
6
6
|
"url": "https://github.com/latticexyz/mud.git",
|
@@ -28,15 +28,14 @@
|
|
28
28
|
"dependencies": {
|
29
29
|
"mobx": "^6.7.0",
|
30
30
|
"rxjs": "7.5.5",
|
31
|
-
"@latticexyz/schema-type": "2.2.
|
32
|
-
"@latticexyz/utils": "2.2.
|
31
|
+
"@latticexyz/schema-type": "2.2.16-59389b1e37bc84664972231989ce7fdc739cce42",
|
32
|
+
"@latticexyz/utils": "2.2.16-59389b1e37bc84664972231989ce7fdc739cce42"
|
33
33
|
},
|
34
34
|
"devDependencies": {
|
35
35
|
"@types/jest": "^27.4.1",
|
36
36
|
"@types/uuid": "^8.3.4",
|
37
37
|
"jest": "^29.3.1",
|
38
38
|
"ts-jest": "^29.0.5",
|
39
|
-
"tsup": "^6.7.0",
|
40
39
|
"type-fest": "^2.14.0"
|
41
40
|
},
|
42
41
|
"scripts": {
|