@lastolivegames/becsy
Advanced tools
Comparing version 0.1.0 to 0.2.0
135
index.d.ts
@@ -1,38 +0,1 @@ | ||
declare class Indexer { | ||
private readonly maxRefs; | ||
private readonly buffer; | ||
private readonly index; | ||
private numRefs; | ||
constructor(maxRefs: number); | ||
/** | ||
* Inserts a new reference entry into the index, but fails if this exact pair is already indexed. | ||
*/ | ||
insert(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Removes a reference entry from the index, failing if it's missing. | ||
*/ | ||
remove(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Returns an iterable over the IDs of all entities that refer to the given one. | ||
*/ | ||
iterateReferrers(referenceId: EntityId): Iterable<EntityId>; | ||
/** | ||
* Finds the entry that matches referencedId:referrerId exactly. If not found, finds the first | ||
* entry that would follow referenceId:referrerId. If referrerId is not specified, finds the | ||
* first referenceId:* entry, if any. | ||
*/ | ||
private findIndex; | ||
} | ||
/** | ||
* A fixed but arbitrary size bitset. | ||
*/ | ||
declare class Bitset { | ||
private readonly size; | ||
private readonly bytes; | ||
constructor(size: number); | ||
get(index: number): boolean; | ||
set(index: number): void; | ||
unset(index: number): void; | ||
clear(): void; | ||
} | ||
interface LogPointer { | ||
@@ -215,3 +178,2 @@ index: number; | ||
private processedEntities; | ||
removedEntities: Bitset; | ||
private shapeLogPointer; | ||
@@ -226,10 +188,9 @@ private writeLogPointer; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defs: DefsArray; | ||
declare class ComponentStats { | ||
_numEntities: number; | ||
maxEntities: number; | ||
capacity: number; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
toString(): string; | ||
} | ||
@@ -240,22 +201,40 @@ declare class Stats { | ||
maxEntities: number; | ||
_maxLimboEntities: number; | ||
_maxLimboComponents: number; | ||
_numRefs: number; | ||
maxRefs: number; | ||
_maxShapeChangesPerFrame: number; | ||
_maxWritesPerFrame: number; | ||
components: { | ||
[typeName: string]: ComponentStats; | ||
}; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
_maxLimboEntities: number; | ||
get maxLimboEntities(): number; | ||
set maxLimboEntities(value: number); | ||
_numRefs: number; | ||
maxRefs: number; | ||
get maxLimboComponents(): number; | ||
set maxLimboComponents(value: number); | ||
get numRefs(): number; | ||
set numRefs(value: number); | ||
_maxShapeChangesPerFrame: number; | ||
get maxShapeChangesPerFrame(): number; | ||
set maxShapeChangesPerFrame(value: number); | ||
_maxWritesPerFrame: number; | ||
get maxWritesPerFrame(): number; | ||
set maxWritesPerFrame(value: number); | ||
for(type: ComponentType<any>): ComponentStats; | ||
toString(): string; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
defs: DefsArray; | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxLimboComponents?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defaultComponentStorage?: ComponentStorage; | ||
} | ||
declare class Dispatcher { | ||
readonly maxEntities: number; | ||
readonly indexer: Indexer; | ||
readonly defaultComponentStorage: ComponentStorage; | ||
readonly registry: Registry; | ||
@@ -272,3 +251,3 @@ readonly systems: SystemBox[]; | ||
private readonly callbackSystem; | ||
constructor({ defs, maxEntities, maxLimboEntities, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame }: WorldOptions); | ||
constructor({ defs, maxEntities, maxLimboEntities, maxLimboComponents, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame, defaultComponentStorage }: WorldOptions); | ||
private normalizeAndInitSystems; | ||
@@ -299,2 +278,3 @@ private splitDefs; | ||
private readonly shapes; | ||
private readonly staleShapes; | ||
private readonly entityIdPool; | ||
@@ -306,7 +286,13 @@ readonly pool: EntityPool; | ||
private readonly oldDeletionPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
private readonly removalLog; | ||
private readonly prevRemovalPointer; | ||
private readonly oldRemovalPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, maxLimboComponents: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
createEntity(initialComponents: (ComponentType<any> | any)[]): Entity; | ||
queueDeletion(id: EntityId): void; | ||
queueRemoval(id: EntityId, type: ComponentType<any>): void; | ||
flush(): void; | ||
processEndOfFrame(): void; | ||
private processDeletionLog; | ||
private processRemovalLog; | ||
extendMaskAndSetFlag(mask: number[], type: ComponentType<any>): void; | ||
@@ -350,3 +336,4 @@ maskHasFlag(mask: number[] | undefined, type: ComponentType<any>): boolean; | ||
constructor(defaultValue: JSType); | ||
abstract define<C>(binding: Binding<C>, name: string, buffer?: SharedArrayBuffer): SharedArrayBuffer; | ||
abstract defineElastic(binding: Binding<any>, field: Field<any>): void; | ||
abstract defineFixed(binding: Binding<any>, field: Field<any>): void; | ||
static boolean: Type<boolean>; | ||
@@ -364,2 +351,4 @@ static uint8: Type<number>; | ||
static ref: Type<Entity | null>; | ||
static object: Type<any>; | ||
static weakObject: Type<any>; | ||
} | ||
@@ -373,2 +362,8 @@ interface SchemaDef<JSType> { | ||
} | ||
type ComponentStorage = "sparse" | "packed" | "compact"; | ||
interface ComponentOptions { | ||
storage?: ComponentStorage; | ||
capacity?: number; | ||
initialCapacity?: number; | ||
} | ||
interface Field<JSType> { | ||
@@ -378,2 +373,5 @@ name: string; | ||
default: JSType; | ||
buffer?: SharedArrayBuffer; | ||
localBuffer?: any[]; | ||
updateBuffer?(): void; | ||
} | ||
@@ -383,19 +381,27 @@ interface ComponentType<C> { | ||
schema?: Schema; | ||
maxEntities?: number; | ||
__id?: number; | ||
__flagOffset?: number; | ||
__flagMask?: number; | ||
__trackedWrites?: boolean; | ||
__fields?: Field<any>[]; | ||
options?: ComponentOptions; | ||
/** | ||
* A unique, sequential id number for this component type, assigned automatically by becsy. It | ||
* will stay the same across runs as long as the list of defs used to create the world doesn't | ||
* change. Feel free to use this for your own purposes but don't change it. | ||
*/ | ||
id?: number; | ||
__binding?: Binding<C>; | ||
__bind?(id: EntityId, writable: boolean): C; | ||
__create?(id: EntityId): C; | ||
__delete?(id: EntityId): void; | ||
} | ||
declare class Binding<C> { | ||
readonly type: ComponentType<C>; | ||
readonly fields: Field<any>[]; | ||
readonly dispatcher: Dispatcher; | ||
readonly maxEntities: number; | ||
capacity: number; | ||
readonly readonlyInstance: C; | ||
readonly writableInstance: C; | ||
readonly flagOffset: number; | ||
readonly flagMask: number; | ||
trackedWrites: boolean; | ||
entityId: number; | ||
index: number; | ||
constructor(type: ComponentType<C>, dispatcher: Dispatcher, maxEntities: number); | ||
constructor(type: ComponentType<C>, fields: Field<any>[], dispatcher: Dispatcher, capacity: number); | ||
} | ||
@@ -408,3 +414,3 @@ declare class World { | ||
execute(time?: number, delta?: number): void; | ||
get stats(): any; | ||
get stats(): Stats; | ||
} | ||
@@ -418,3 +424,4 @@ interface PropOptions<JSType> { | ||
declare function component(constructor: ComponentType<any>): void; | ||
declare function component(options: ComponentOptions): (constructor: ComponentType<any>) => void; | ||
export { World, Type, Entity, System, SystemType, componentTypes, component, prop, Query, ComponentType }; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -1,38 +0,1 @@ | ||
declare class Indexer { | ||
private readonly maxRefs; | ||
private readonly buffer; | ||
private readonly index; | ||
private numRefs; | ||
constructor(maxRefs: number); | ||
/** | ||
* Inserts a new reference entry into the index, but fails if this exact pair is already indexed. | ||
*/ | ||
insert(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Removes a reference entry from the index, failing if it's missing. | ||
*/ | ||
remove(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Returns an iterable over the IDs of all entities that refer to the given one. | ||
*/ | ||
iterateReferrers(referenceId: EntityId): Iterable<EntityId>; | ||
/** | ||
* Finds the entry that matches referencedId:referrerId exactly. If not found, finds the first | ||
* entry that would follow referenceId:referrerId. If referrerId is not specified, finds the | ||
* first referenceId:* entry, if any. | ||
*/ | ||
private findIndex; | ||
} | ||
/** | ||
* A fixed but arbitrary size bitset. | ||
*/ | ||
declare class Bitset { | ||
private readonly size; | ||
private readonly bytes; | ||
constructor(size: number); | ||
get(index: number): boolean; | ||
set(index: number): void; | ||
unset(index: number): void; | ||
clear(): void; | ||
} | ||
interface LogPointer { | ||
@@ -215,3 +178,2 @@ index: number; | ||
private processedEntities; | ||
removedEntities: Bitset; | ||
private shapeLogPointer; | ||
@@ -226,10 +188,9 @@ private writeLogPointer; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defs: DefsArray; | ||
declare class ComponentStats { | ||
_numEntities: number; | ||
maxEntities: number; | ||
capacity: number; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
toString(): string; | ||
} | ||
@@ -240,22 +201,40 @@ declare class Stats { | ||
maxEntities: number; | ||
_maxLimboEntities: number; | ||
_maxLimboComponents: number; | ||
_numRefs: number; | ||
maxRefs: number; | ||
_maxShapeChangesPerFrame: number; | ||
_maxWritesPerFrame: number; | ||
components: { | ||
[typeName: string]: ComponentStats; | ||
}; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
_maxLimboEntities: number; | ||
get maxLimboEntities(): number; | ||
set maxLimboEntities(value: number); | ||
_numRefs: number; | ||
maxRefs: number; | ||
get maxLimboComponents(): number; | ||
set maxLimboComponents(value: number); | ||
get numRefs(): number; | ||
set numRefs(value: number); | ||
_maxShapeChangesPerFrame: number; | ||
get maxShapeChangesPerFrame(): number; | ||
set maxShapeChangesPerFrame(value: number); | ||
_maxWritesPerFrame: number; | ||
get maxWritesPerFrame(): number; | ||
set maxWritesPerFrame(value: number); | ||
for(type: ComponentType<any>): ComponentStats; | ||
toString(): string; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
defs: DefsArray; | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxLimboComponents?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defaultComponentStorage?: ComponentStorage; | ||
} | ||
declare class Dispatcher { | ||
readonly maxEntities: number; | ||
readonly indexer: Indexer; | ||
readonly defaultComponentStorage: ComponentStorage; | ||
readonly registry: Registry; | ||
@@ -272,3 +251,3 @@ readonly systems: SystemBox[]; | ||
private readonly callbackSystem; | ||
constructor({ defs, maxEntities, maxLimboEntities, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame }: WorldOptions); | ||
constructor({ defs, maxEntities, maxLimboEntities, maxLimboComponents, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame, defaultComponentStorage }: WorldOptions); | ||
private normalizeAndInitSystems; | ||
@@ -299,2 +278,3 @@ private splitDefs; | ||
private readonly shapes; | ||
private readonly staleShapes; | ||
private readonly entityIdPool; | ||
@@ -306,7 +286,13 @@ readonly pool: EntityPool; | ||
private readonly oldDeletionPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
private readonly removalLog; | ||
private readonly prevRemovalPointer; | ||
private readonly oldRemovalPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, maxLimboComponents: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
createEntity(initialComponents: (ComponentType<any> | any)[]): Entity; | ||
queueDeletion(id: EntityId): void; | ||
queueRemoval(id: EntityId, type: ComponentType<any>): void; | ||
flush(): void; | ||
processEndOfFrame(): void; | ||
private processDeletionLog; | ||
private processRemovalLog; | ||
extendMaskAndSetFlag(mask: number[], type: ComponentType<any>): void; | ||
@@ -350,3 +336,4 @@ maskHasFlag(mask: number[] | undefined, type: ComponentType<any>): boolean; | ||
constructor(defaultValue: JSType); | ||
abstract define<C>(binding: Binding<C>, name: string, buffer?: SharedArrayBuffer): SharedArrayBuffer; | ||
abstract defineElastic(binding: Binding<any>, field: Field<any>): void; | ||
abstract defineFixed(binding: Binding<any>, field: Field<any>): void; | ||
static boolean: Type<boolean>; | ||
@@ -364,2 +351,4 @@ static uint8: Type<number>; | ||
static ref: Type<Entity | null>; | ||
static object: Type<any>; | ||
static weakObject: Type<any>; | ||
} | ||
@@ -373,2 +362,8 @@ interface SchemaDef<JSType> { | ||
} | ||
type ComponentStorage = "sparse" | "packed" | "compact"; | ||
interface ComponentOptions { | ||
storage?: ComponentStorage; | ||
capacity?: number; | ||
initialCapacity?: number; | ||
} | ||
interface Field<JSType> { | ||
@@ -378,2 +373,5 @@ name: string; | ||
default: JSType; | ||
buffer?: SharedArrayBuffer; | ||
localBuffer?: any[]; | ||
updateBuffer?(): void; | ||
} | ||
@@ -383,19 +381,27 @@ interface ComponentType<C> { | ||
schema?: Schema; | ||
maxEntities?: number; | ||
__id?: number; | ||
__flagOffset?: number; | ||
__flagMask?: number; | ||
__trackedWrites?: boolean; | ||
__fields?: Field<any>[]; | ||
options?: ComponentOptions; | ||
/** | ||
* A unique, sequential id number for this component type, assigned automatically by becsy. It | ||
* will stay the same across runs as long as the list of defs used to create the world doesn't | ||
* change. Feel free to use this for your own purposes but don't change it. | ||
*/ | ||
id?: number; | ||
__binding?: Binding<C>; | ||
__bind?(id: EntityId, writable: boolean): C; | ||
__create?(id: EntityId): C; | ||
__delete?(id: EntityId): void; | ||
} | ||
declare class Binding<C> { | ||
readonly type: ComponentType<C>; | ||
readonly fields: Field<any>[]; | ||
readonly dispatcher: Dispatcher; | ||
readonly maxEntities: number; | ||
capacity: number; | ||
readonly readonlyInstance: C; | ||
readonly writableInstance: C; | ||
readonly flagOffset: number; | ||
readonly flagMask: number; | ||
trackedWrites: boolean; | ||
entityId: number; | ||
index: number; | ||
constructor(type: ComponentType<C>, dispatcher: Dispatcher, maxEntities: number); | ||
constructor(type: ComponentType<C>, fields: Field<any>[], dispatcher: Dispatcher, capacity: number); | ||
} | ||
@@ -408,3 +414,3 @@ declare class World { | ||
execute(time?: number, delta?: number): void; | ||
get stats(): any; | ||
get stats(): Stats; | ||
} | ||
@@ -418,3 +424,4 @@ interface PropOptions<JSType> { | ||
declare function component(constructor: ComponentType<any>): void; | ||
declare function component(options: ComponentOptions): (constructor: ComponentType<any>) => void; | ||
export { World, Type, Entity, System, SystemType, componentTypes, component, prop, Query, ComponentType }; | ||
//# sourceMappingURL=index.min.d.ts.map |
@@ -1,2 +0,2 @@ | ||
const e=new TextEncoder,t=new TextDecoder;function s(e){throw new Error(`Component is not writable; use entity.write(${e.type.name}) to acquire a writable version`)}class r{defaultValue;constructor(e){this.defaultValue=e}static boolean;static uint8;static int8;static uint16;static int16;static uint32;static int32;static float32;static float64;static staticString;static dynamicString;static ref}class i extends r{NumberArray;constructor(e){super(0),this.NumberArray=e}define(e,t,r){r||(r=new SharedArrayBuffer(e.maxEntities*this.NumberArray.BYTES_PER_ELEMENT));const i=new this.NumberArray(r);return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){i[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){s(e)}}),r}}class n extends r{choices;choicesIndex=new Map;TypedArray;constructor(e){if(super(e[0]),this.choices=e,!e?.length)throw new Error("No choices specified for Type.staticString");e.length<256?this.TypedArray=Uint8Array:e.length<65536?this.TypedArray=Uint16Array:this.TypedArray=Uint32Array;for(let t=0;t<e.length;t++)this.choicesIndex.set(e[t],t)}define(e,t,r){r||(r=new SharedArrayBuffer(e.maxEntities*this.TypedArray.BYTES_PER_ELEMENT));const i=new this.TypedArray(r),n=this.choices,a=this.choicesIndex;return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){const s=a.get(t);if(void 0===s)throw new Error(`Static string not in set: "${t}"`);i[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){s(e)}}),r}}class a extends r{maxUtf8Length;lengthsStride;bytesStride;constructor(e){super(""),this.maxUtf8Length=e+e%2,this.lengthsStride=e/2+1,this.bytesStride=this.maxUtf8Length+2}define(r,i,n){if(!n){const e=r.maxEntities*(this.maxUtf8Length+Uint16Array.BYTES_PER_ELEMENT);n=new SharedArrayBuffer(e)}const a=new Uint16Array(n),o=new Uint8Array(n),h=this.maxUtf8Length,d=this.lengthsStride,c=this.bytesStride;return Object.defineProperty(r.writableInstance,i,{enumerable:!0,configurable:!0,get(){const e=a[r.index*d];return t.decode(new Uint8Array(o.buffer,r.index*c+2,e))},set(t){const s=e.encode(t);if(s.byteLength>h)throw new Error(`Dynamic string length > ${h} after encoding: ${t}`);a[r.index*d]=s.byteLength,o.set(s,r.index*c+2)}}),Object.defineProperty(r.readonlyInstance,i,{enumerable:!0,configurable:!0,get(){const e=a[r.index*d];return t.decode(new Uint8Array(o.buffer,r.index*c+2,e))},set(e){s(r)}}),n}}r.boolean=new class extends r{constructor(){super(!1)}define(e,t,r){r||(r=new SharedArrayBuffer(e.maxEntities));const i=new Uint8Array(r);return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){i[e.index]=t?1:0}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){s(e)}}),r}},r.uint8=new i(Uint8Array),r.int8=new i(Int8Array),r.uint16=new i(Uint16Array),r.int16=new i(Int16Array),r.uint32=new i(Uint32Array),r.int32=new i(Int32Array),r.float32=new i(Float32Array),r.float64=new i(Float64Array),r.staticString=e=>new n(e),r.dynamicString=e=>new a(e),r.ref=new class extends r{constructor(){super(null)}define(e,t,r){r||(r=new SharedArrayBuffer(e.maxEntities*Int32Array.BYTES_PER_ELEMENT));const i=new Int32Array(r);return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){const s=i[e.index],r=t?.__id??-1;if(s===r)return;const n=e.dispatcher.indexer;0!==s&&n.remove(s,e.entityId),i[e.index]=r,0!==r&&n.insert(r,e.entityId)}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){s(e)}}),r}};class o{type;dispatcher;maxEntities;readonlyInstance;writableInstance;entityId=0;index=0;constructor(e,t,s){this.type=e,this.dispatcher=t,this.maxEntities=s,this.readonlyInstance=new e,this.writableInstance=new e}}function h(e,t,s){if((t.maxEntities??0)>s.maxEntities)throw new Error(`Component type ${t.name} maxEntities higher than world maxEntities; reduce ${t.maxEntities} to or below ${s.maxEntities}`);if(("undefined"==typeof process||"test"!==process.env.NODE_ENV)&&t.__bind)throw new Error(`Component type ${t.name} is already in use in another world`);const i=Math.min(t.maxEntities??s.maxEntities,s.maxEntities);s.maxEntities,t.__id=e,t.__flagOffset=e>>5,t.__flagMask=1<<(31&e);const n=new o(t,s,i);t.__bind=(e,t)=>(n.entityId=e,n.index=e,t?n.writableInstance:n.readonlyInstance),t.__fields=function(e){const t=e.schema,s=[];for(const e in t){const i=t[e];let n;n=i instanceof r?{name:e,default:i.defaultValue,type:i}:Object.assign({name:e,default:i.type.defaultValue},i),s.push(n)}return s}(t);for(const e of t.__fields)e.type.define(n,e.name)}const d=Object.freeze({});class c{__registry;__id;joined;constructor(e){this.__registry=e}__reset(e){this.__id=e,this.joined=d}add(e,t){if(this.__checkMask(e,!0),this.__registry.hasFlag(this.__id,e))throw new Error(`Entity already has a ${e.name} component`);return this.__registry.setFlag(this.__id,e),function(e,t,s){if(void 0!==s)for(const t in s)if(!e.schema?.[t])throw new Error(`Property ${t} not defined for component ${e.name}`);const r=e.__bind(t,!0);for(const t of e.__fields)r[t.name]=s?.[t.name]??t.default}(e,this.__id,t),this}addAll(...e){for(let t=0;t<e.length;t++){const s=e[t];if("function"!=typeof s)throw new Error(`Bad arguments to bulk add: expected component type, got: ${s}`);let r=e[t+1];"function"==typeof r?r=void 0:t++,this.add(s,r)}return this}remove(e){if(!this.has(e))throw new Error(`Entity doesn't have a ${e.name} component`);this.__remove(e)}removeAll(...e){for(const t of e)this.remove(t)}has(e,t=!1){return this.__checkMask(e,!1),this.__registry.hasFlag(this.__id,e,t)}read(e){if(!this.has(e))throw new Error(`Entity doesn't have a ${e.name} component`);return e.__bind(this.__id,!1)}readRecentlyRemoved(e){if(!this.has(e,!0))throw new Error(`Entity doesn't have a ${e.name} component`);return e.__bind(this.__id,!1)}write(e){if(!this.has(e,!0))throw new Error(`Entity doesn't have a ${e.name} component`);return e.__trackedWrites&&this.__registry.trackWrite(this.__id,e),e.__bind(this.__id,!0)}delete(){for(const e of this.__registry.types)this.__registry.hasFlag(this.__id,e)&&(this.__checkMask(e,!0),this.__remove(e));this.__registry.queueDeletion(this.__id),this.__wipeInboundRefs()}__remove(e){this.__deindexOutboundRefs(e),this.__registry.clearFlag(this.__id,e)}__deindexOutboundRefs(e){const t=e.__fields;if(t.some((e=>e.type===r.ref))){const s=this.write(e);for(const e of t)e.type===r.ref&&(s[e.name]=null)}}__wipeInboundRefs(){}__checkMask(e,t){const s=this.__registry.executingSystem?.rwMasks,r=t?s?.write:s?.read;if(r&&!this.__registry.maskHasFlag(r,e))throw new Error(`System didn't mark component ${e.name} as ${t?"writable":"readable"}`)}}class l{maxRefs;buffer;index;numRefs=0;constructor(e){this.maxRefs=e,this.index=new Uint32Array(this.buffer=new SharedArrayBuffer(8*e))}insert(e,t){if(this.numRefs>=this.maxRefs)throw new Error(`Max number of refs reached: ${this.maxRefs}`);const s=this.findIndex(e,t);if(this.index[2*s]===e&&this.index[2*s+1]===t)throw new Error(`Internal error; ref already indexed: ${t} -> ${e}`);this.index.copyWithin(2*(s+1),2*s,2*(this.numRefs+1)),this.index[2*s]=e,this.index[2*s+1]=t,this.numRefs+=1}remove(e,t){const s=this.findIndex(e,t);if(this.index[2*s]===e&&this.index[2*s+1]===t)throw new Error(`Internal error; ref not found: ${t} -> ${e}`);this.numRefs-=1,this.index.copyWithin(s,s+1,this.numRefs-s)}*iterateReferrers(e){}findIndex(e,t){let s=0,r=this.numRefs-1,i=1;for(;s<r;){i=Math.floor((r-s+1)/2)+s;const n=this.index[2*i];n===e?void 0!==t&&this.index[2*i+1]<t?s=i+1:r=i-1:n<e?s=i+1:r=i-1}return s>=r&&(i=Math.max(0,Math.min(this.numRefs+1,s))),i}}class u{size;bytes;constructor(e){this.size=e,this.bytes=new Uint32Array(Math.ceil(e/32))}get(e){if(e<0||e>=this.size)throw new Error(`Bit index out of bounds: ${e}`);return 0!=(this.bytes[e>>>5]&1<<(31&e))}set(e){if(e<0||e>=this.size)throw new Error(`Bit index out of bounds: ${e}`);this.bytes[e>>>5]|=1<<(31&e)}unset(e){if(e<0||e>=this.size)throw new Error(`Bit index out of bounds: ${e}`);this.bytes[e>>>5]&=~(1<<(31&e))}clear(){this.bytes.fill(0)}}const m=[];class f{maxEntries;configParamName;data;corral;constructor(e,t){this.maxEntries=e,this.configParamName=t;const s=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.data=new Uint32Array(s);const r=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.corral=new Uint32Array(r)}push(e){const t=this.corral[0];t>=this.maxEntries&&this.throwCapacityExceeded(),t&&this.corral[t]===e||(this.corral[t+2]=e,this.corral[0]+=1)}commit(){const e=this.corral[0];if(!e)return;let t=this.data[0];const s=Math.min(e,this.maxEntries-t);for(this.data.set(this.corral.subarray(2,s+2),t+2),s<e&&this.data.set(this.corral.subarray(s+2,e+2),2),t+=e;t>=this.maxEntries;)t-=this.maxEntries,this.data[1]+=1;this.data[0]=t,this.corral[0]=0,this.corral[1]+=1}createPointer(e){return e?(e.index=this.data[0],e.generation=this.data[1],e.corralIndex=this.corral[0],e.corralGeneration=this.corral[1],e):{index:this.data[0],generation:this.data[1],corralIndex:this.corral[0],corralGeneration:this.corral[1]}}hasUpdatesSince(e){return this.checkPointer(e),!(e.index===this.data[0]&&e.generation===this.data[1]&&(e.corralGeneration===this.corral[1]?e.corralIndex===this.corral[0]:0===this.corral[0]))}processSince(e,t){this.checkPointers(e,t);let s=m;const r=t?.index??this.data[0],i=t?.generation??this.data[1];if(e.generation===i)if(e.index<r)s=[this.data,e.index+2,r+2],e.index=r;else{const t=this.corral[0],r=this.corral[1];(e.corralGeneration===r?e.corralIndex<t:t)&&(s=[this.corral,e.corralIndex+2,t+2],e.corralIndex=t,e.corralGeneration=r)}else s=[this.data,e.index+2,this.data.length],e.index=0,e.generation=i;return s}countSince(e,t){if(this.checkPointers(e,t),this.corral[0])throw new Error("Internal error, should commit log before counting");const s=e.index,r=t?.index??this.data[0],i=t?.generation??this.data[1];return e.index=r,e.generation=i,s===r&&e.generation===i?0:s<r?r-s:this.maxEntries-(s-r)}checkPointers(e,t){if(this.checkPointer(e),t&&(this.checkPointer(t),e.index>t.index&&e.generation>=t.generation))throw new RangeError("Internal error, start pointer exceeds end pointer")}checkPointer(e){const t=this.data[0];let s=e.generation;if(e.index===t?s+1<this.data[1]&&this.throwCapacityExceeded():(e.index>t&&(s+=1),s!==this.data[1]&&this.throwCapacityExceeded()),e.corralGeneration>this.corral[1])throw new Error("Internal error, pointer corral generation older than corral");if(e.corralGeneration===this.corral[1]&&e.corralIndex>this.corral[0])throw new Error("Internal error, pointer past end of log corral area")}throwCapacityExceeded(){throw new Error(`Log capacity exceeded, please raise ${this.configParamName} above ${this.maxEntries}`)}}class _{maxItems;configParamName;data;constructor(e,t){this.maxItems=e,this.configParamName=t,this.data=new Uint32Array(new SharedArrayBuffer((e+1)*Uint32Array.BYTES_PER_ELEMENT))}get length(){return this.data[0]}take(){const e=this.data[0]--;if(e<=0)throw new RangeError(`Pool capacity exceeded, please raise ${this.configParamName} above ${this.maxItems}`);return this.data[e]}refill(e){if(!e.length)return;const t=this.length,s=t+e.length;if(s>this.maxItems)throw new Error("Internal error, refill exceeded pool capacity");this.data.set(e,t+1),this.data[0]=s}fillWithDescendingIntegers(e){const t=this.length;for(let s=this.data.length-1;s>t;s--)this.data[s]=e++;this.data[0]=this.data.length-1}}class g{pool;entities=[];constructor(e){this.pool=e}add(e){this.entities.push(this.pool.borrowTemporarily(e))}clear(){this.entities.length&&this.entities.splice(0,1/0)}}class y{pool;entities=[];lookupTable;constructor(e,t){this.pool=e,this.lookupTable=new Int32Array(t),this.lookupTable.fill(-1)}add(e){const t=this.entities.push(this.pool.borrow(e))-1;this.lookupTable[e]=t}remove(e){const t=this.lookupTable[e];if(t<0)throw new Error("Internal error, entity not in list");this.pool.return(e),this.lookupTable[e]=-1;const s=this.entities.pop();t<this.entities.length&&(this.entities[t]=s,this.lookupTable[s.__id]=t)}has(e){return this.lookupTable[e]>=0}clear(){throw new Error("Internal error, trying to clear persistent entity list")}}var p;!function(e){e[e.all=1]="all",e[e.added=2]="added",e[e.removed=4]="removed",e[e.changed=8]="changed",e[e.addedOrChanged=16]="addedOrChanged",e[e.changedOrRemoved=32]="changedOrRemoved",e[e.addedChangedOrRemoved=64]="addedChangedOrRemoved"}(p||(p={}));const w=p.added|p.removed|p.changed|p.addedOrChanged|p.changedOrRemoved|p.addedChangedOrRemoved,x=p.changed|p.addedOrChanged|p.changedOrRemoved|p.addedChangedOrRemoved;class E{query;system;results={};flavors=0;__withMask;__withoutMask;__trackMask;__refMask;hasTransientResults;hasChangedResults;currentEntities;changedEntities;constructor(e,t){this.query=e,this.system=t,e.__results=this.results,e.__systemName=t.name}complete(){const e=this.system.dispatcher;if(this.hasTransientResults=Boolean(this.flavors&w),this.hasChangedResults=Boolean(this.flavors&x),this.hasChangedResults&&!this.__trackMask)throw new Error("Query for changed entities must track at least one component");this.flavors&p.all?this.results.all=new y(e.registry.pool,e.maxEntities):this.currentEntities=new u(e.maxEntities),this.hasTransientResults&&this.allocateTransientResultLists(),this.flavors&&this.system.shapeQueries.push(this),this.hasChangedResults&&(this.changedEntities=new u(e.maxEntities),this.system.writeQueries.push(this))}allocateTransientResultLists(){this.flavors&p.added&&this.allocateResult("added"),this.flavors&p.removed&&this.allocateResult("removed"),this.flavors&p.changed&&this.allocateResult("changed"),this.flavors&p.addedOrChanged&&this.allocateResult("addedOrChanged"),this.flavors&p.changedOrRemoved&&this.allocateResult("changedOrRemoved"),this.flavors&p.addedChangedOrRemoved&&this.allocateResult("addedChangedOrRemoved")}allocateResult(e){const t=this.system.dispatcher;this.results[e]=new g(t.registry.pool)}clearTransientResults(){this.hasTransientResults&&(this.results.added?.clear(),this.results.removed?.clear(),this.results.changed?.clear(),this.results.addedOrChanged?.clear(),this.results.changedOrRemoved?.clear(),this.results.addedChangedOrRemoved?.clear(),this.changedEntities?.clear())}handleShapeUpdate(e){const t=this.system.dispatcher.registry,s=this.results.all?.has(e)??this.currentEntities.get(e),r=t.matchShape(e,this.__withMask,this.__withoutMask);r&&!s?(this.currentEntities?.set(e),this.results.all?.add(e),this.results.added?.add(e),this.results.addedOrChanged?.add(e),this.results.addedChangedOrRemoved?.add(e)):!r&&s&&(this.currentEntities?.unset(e),this.system.removedEntities.set(e),this.results.all?.remove(e),this.results.removed?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}handleWrite(e,t,s){!this.changedEntities.get(e)&&(this.__trackMask[t]??0)&s&&(this.changedEntities.set(e),this.results.changed?.add(e),this.results.addedOrChanged?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}}class b{__callback;__userQuery;__query;__system;__lastTypes;constructor(e,t){this.__callback=e,this.__userQuery=t}__build(e){try{this.__system=e,this.__query=new E(this.__userQuery,e),this.__callback(this),this.__query.complete()}catch(t){throw t.message=`Failed to build query in system ${e.name}: ${t.message}`,t}}get and(){return this}get but(){return this}get also(){return this}get all(){return this.__query.flavors|=p.all,this}get added(){return this.__query.flavors|=p.added,this}get removed(){return this.__query.flavors|=p.removed,this}get changed(){return this.__query.flavors|=p.changed,this}get addedOrChanged(){return this.__query.flavors|=p.addedOrChanged,this}get changedOrRemoved(){return this.__query.flavors|=p.changedOrRemoved,this}get addedChangedOrRemoved(){return this.__query.flavors|=p.addedChangedOrRemoved,this}with(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withMask"),this}without(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withoutMask",e),this}using(...e){return this.set(this.__system.rwMasks.read,e),this}get track(){this.set("__trackMask");for(const e of this.__lastTypes)e.__trackedWrites=!0;return this}get read(){return this}get write(){return this.set(this.__system.rwMasks.write),this}set(e,t,s){if(e){if(t||(t=this.__lastTypes),!t)throw new Error("No component type to apply query modifier to");if(this.__lastTypes=t,"string"==typeof e){if(s&&this.__query[e])throw new Error(`Only one ${s} allowed`);this.__query[e]||(this.__query[e]=[]),e=this.__query[e]}else if(s&&e.some((e=>0!==e)))throw new Error(`Only one ${s} allowed`);for(const s of t)this.__system.dispatcher.registry.extendMaskAndSetFlag(e,s)}}}class v{__results;__systemName;get all(){return this.__checkList("all"),this.__results.all.entities}get added(){return this.__checkList("added"),this.__results.added.entities}get removed(){return this.__checkList("removed"),this.__results.removed.entities}get changed(){return this.__checkList("changed"),this.__results.changed.entities}get addedOrChanged(){return this.__checkList("addedOrChanged"),this.__results.addedOrChanged.entities}get changedOrRemoved(){return this.__checkList("changedOrRemoved"),this.__results.changedOrRemoved.entities}get addedChangedOrRemoved(){return this.__checkList("addedChangedOrRemoved"),this.__results.addedChangedOrRemoved.entities}__checkList(e){if(!this.__results[e])throw new Error(`Query '${e}' not configured, please add .${e} to your query definition in system ${this.__systemName}`)}}class R{__queryBuilders=[];__dispatcher;time;delta;get name(){return this.constructor.name}query(e){const t=new v,s=new b(e,t);if(!this.__queryBuilders)throw new Error(`Attempt to create a new query after world initialized in system ${this.name}`);return this.__queryBuilders.push(s),t}createEntity(...e){return this.__dispatcher.createEntity(e)}}class k{system;dispatcher;rwMasks={read:[],write:[]};shapeQueries=[];writeQueries=[];hasWriteQueries;processedEntities;removedEntities;shapeLogPointer;writeLogPointer;get name(){return this.system.name}constructor(e,t){this.system=e,this.dispatcher=t,e.__dispatcher=t,this.shapeLogPointer=t.shapeLog.createPointer(),this.writeLogPointer=t.writeLog?.createPointer(),this.processedEntities=new u(t.maxEntities),this.removedEntities=new u(t.maxEntities);for(const t of e.__queryBuilders)t.__build(this);e.__queryBuilders=null,this.hasWriteQueries=!!this.writeQueries.length}execute(e,t){this.system.time=e,this.system.delta=t,this.runQueries(),this.system.execute()}runQueries(){const e=this.dispatcher.shapeLog.hasUpdatesSince(this.shapeLogPointer),t=this.hasWriteQueries&&this.dispatcher.writeLog.hasUpdatesSince(this.writeLogPointer);if(e||t){this.processedEntities.clear();for(const e of this.shapeQueries)e.clearTransientResults();e&&this.__updateShapeQueries(),t&&this.__updateWriteQueries()}}__updateShapeQueries(){const e=this.dispatcher.shapeLog;let t,s,r;for(;[t,s,r]=e.processSince(this.shapeLogPointer),t;)for(let e=s;e<r;e++){const s=t[e];if(!this.processedEntities.get(s)){this.processedEntities.set(s);for(const e of this.shapeQueries)e.handleShapeUpdate(s)}}}__updateWriteQueries(){const e=this.dispatcher.writeLog;let t,s,r;for(;[t,s,r]=e.processSince(this.writeLogPointer),t;)for(let e=s;e<r;e++){const s=t[e],r=4194303&s;if(!this.processedEntities.get(r)){const e=s>>>22;for(const t of this.writeQueries)t.handleWrite(r,e>>5,1<<(31&e))}}}}class P{registry;borrowed;borrowCounts;spares=[];temporarilyBorrowedIds=[];constructor(e,t){this.registry=e,this.borrowed=Array.from({length:t}),this.borrowCounts=new Int32Array(t)}borrow(e){this.borrowCounts[e]+=1;let t=this.borrowed[e];return t||(t=this.borrowed[e]=this.spares.pop()??new c(this.registry),t.__reset(e)),t}borrowTemporarily(e){const t=this.borrow(e);return this.temporarilyBorrowedIds.push(e),t}returnTemporaryBorrows(){for(const e of this.temporarilyBorrowedIds)this.return(e);this.temporarilyBorrowedIds.splice(0,1/0)}return(e){if(!this.borrowCounts[e])throw new Error("Internal error, returning entity with no borrows");--this.borrowCounts[e]<=0&&(this.spares.push(this.borrowed[e]),this.borrowed[e]=void 0)}}class L{types;dispatcher;stride;shapes;entityIdPool;pool;executingSystem;deletionLog;prevDeletionPointer;oldDeletionPointer;constructor(e,t,s,r){this.types=s,this.dispatcher=r;let i=0;for(const e of s)h(i++,e,this.dispatcher);this.stride=Math.ceil(s.length/32);const n=e*this.stride*4;this.shapes=new Uint32Array(new SharedArrayBuffer(n)),this.entityIdPool=new _(e,"maxEntities"),this.entityIdPool.fillWithDescendingIntegers(0),this.pool=new P(this,e),this.deletionLog=new f(t,"maxLimboEntities"),this.prevDeletionPointer=this.deletionLog.createPointer(),this.oldDeletionPointer=this.deletionLog.createPointer()}createEntity(e){const t=this.entityIdPool.take();this.shapes.fill(0,t*this.stride,(t+1)*this.stride);const s=this.pool.borrowTemporarily(t);return e&&s.addAll(...e),this.dispatcher.stats.numEntities+=1,s}queueDeletion(e){this.deletionLog.push(e)}flush(){this.pool.returnTemporaryBorrows(),this.deletionLog.commit()}processEndOfFrame(){this.deletionLog.commit();let e,t,s,r=0;for(;[e,t,s]=this.deletionLog.processSince(this.oldDeletionPointer,this.prevDeletionPointer),e;){const i=e.subarray(t,s);this.entityIdPool.refill(i),r+=i.length}this.dispatcher.stats.numEntities-=r,this.dispatcher.stats.maxLimboEntities=r,this.deletionLog.createPointer(this.prevDeletionPointer)}extendMaskAndSetFlag(e,t){const s=t.__flagOffset;s>=e.length&&(e.length=s+1,e.fill(0,e.length,s)),e[s]|=t.__flagMask}maskHasFlag(e,t){return 0!=((e?.[t.__flagOffset]??0)&t.__flagMask)}hasFlag(e,t,s=!1){const r=e*this.stride+t.__flagOffset;return 0!=(this.shapes[r]&t.__flagMask)||!(!s||!this.executingSystem?.removedEntities.get(e)||0==((this.executingSystem.rwMasks.read?.[t.__flagOffset]??0)&t.__flagMask))}setFlag(e,t){this.shapes[e*this.stride+t.__flagOffset]|=t.__flagMask,this.dispatcher.shapeLog.push(e)}clearFlag(e,t){this.shapes[e*this.stride+t.__flagOffset]&=~t.__flagMask,this.dispatcher.shapeLog.push(e)}trackWrite(e,t){this.dispatcher.writeLog.push(e|t.__id<<22)}matchShape(e,t,s){const r=e*this.stride;if(t)for(let e=0;e<t.length;e++){const s=t[e];if((this.shapes[r+e]&s)!==s)return!1}if(s)for(let e=0;e<s.length;e++){const t=s[e];if(0!=(this.shapes[r+e]&t))return!1}return!0}}const S="undefined"!=typeof window&&void 0!==window.performance?performance.now.bind(performance):Date.now.bind(Date);class I{frames=0;_numEntities=0;maxEntities=0;get numEntities(){return this._numEntities}set numEntities(e){this._numEntities=e,e>this.maxEntities&&(this.maxEntities=e)}_maxLimboEntities=0;get maxLimboEntities(){return this._maxLimboEntities}set maxLimboEntities(e){e>this._maxLimboEntities&&(this._maxLimboEntities=e)}_numRefs=0;maxRefs=0;get numRefs(){return this._numRefs}set numRefs(e){this._numRefs=e,e>this.maxRefs&&(this.maxRefs=e)}_maxShapeChangesPerFrame=0;get maxShapeChangesPerFrame(){return this._maxShapeChangesPerFrame}set maxShapeChangesPerFrame(e){e>this._maxShapeChangesPerFrame&&(this._maxShapeChangesPerFrame=e)}_maxWritesPerFrame=0;get maxWritesPerFrame(){return this._maxWritesPerFrame}set maxWritesPerFrame(e){e>this._maxWritesPerFrame&&(this._maxWritesPerFrame=e)}toString(){return`World stats:\n frames: ${this.frames}\n entities: ${this.numEntities} of ${this.maxEntities} max (${this.maxLimboEntities} limbo max)\n refs: ${this.numRefs} of ${this.maxRefs} max\n logs: ${this.maxShapeChangesPerFrame} shape changes/frame max, ${this.maxWritesPerFrame} writes/frame max`}}class O extends R{__callback;execute(){this.__callback(this)}}class A{maxEntities;indexer;registry;systems;lastTime=S()/1e3;executing;shapeLog;writeLog;shapeLogFramePointer;writeLogFramePointer;stats;userCallbackSystem;callbackSystem;constructor({defs:e,maxEntities:t=1e4,maxLimboEntities:s=Math.ceil(t/5),maxRefs:r=t,maxShapeChangesPerFrame:i=2*t,maxWritesPerFrame:n=4*t}){if(t>4194303)throw new Error("maxEntities too high, the limit is 4194303");const{componentTypes:a,systemTypes:o}=this.splitDefs(e);if(a.length>1024)throw new Error("Too many component types, the limit is 1024");this.stats=new I,this.maxEntities=t,this.shapeLog=new f(i,"maxShapeChangesPerFrame"),this.shapeLogFramePointer=this.shapeLog.createPointer(),this.indexer=new l(r),this.registry=new L(t,s,a.flat(1/0),this),this.systems=this.normalizeAndInitSystems(o),this.systems.some((e=>e.hasWriteQueries))&&(this.writeLog=new f(n,"maxWritesPerFrame"),this.writeLogFramePointer=this.writeLog.createPointer()),this.userCallbackSystem=new O,this.callbackSystem=new k(this.userCallbackSystem,this)}normalizeAndInitSystems(e){const t=[],s=e.flat(1/0);for(let e=0;e<s.length;e++){const r=new s[e],i=s[e+1];i&&"function"!=typeof i&&(Object.assign(r,i),e++),t.push(new k(r,this))}return t}splitDefs(e){const t=[],s=[];let r=!1;for(const i of e.flat(1/0))if("function"==typeof i)r=!i.schema,(r?s:t).push(i);else{if(!r)throw new Error("Unexpected value in world defs: "+i);s.push(i),r=!1}return{componentTypes:t,systemTypes:s}}execute(e,t,s){if(this.executing)throw new Error("Recursive system execution not allowed");this.executing=!0,void 0===e&&(e=S()/1e3),void 0===t&&(t=e-this.lastTime),this.lastTime=e;for(const r of s??this.systems)this.registry.executingSystem=r,r.execute(e,t),this.flush();this.registry.executingSystem=void 0,this.registry.processEndOfFrame(),this.executing=!1,this.gatherFrameStats()}executeFunction(e){if(this.executing)throw new Error("Ad hoc function execution not allowed while world is executing");this.executing=!0,this.userCallbackSystem.__callback=e,this.callbackSystem.execute(0,0),this.flush(),this.registry.processEndOfFrame(),this.executing=!1,this.gatherFrameStats()}gatherFrameStats(){this.stats.frames+=1,this.stats.maxShapeChangesPerFrame=this.shapeLog.countSince(this.shapeLogFramePointer),this.stats.maxWritesPerFrame=this.writeLog?.countSince(this.writeLogFramePointer)??0}flush(){this.registry.flush(),this.shapeLog.commit(),this.writeLog?.commit()}createEntity(e){const t=this.registry.createEntity(e);return this.executing||this.flush(),t}}class C{__dispatcher;constructor(e){this.__dispatcher=new A(e)}build(e){this.__dispatcher.executeFunction(e)}createEntity(...e){this.__dispatcher.createEntity(e)}execute(e,t){this.__dispatcher.execute(e,t)}get stats(){return this.__dispatcher.stats}}function T(e){return function(t,s){t.constructor.schema||(t.constructor.schema={});const r="type"in e?e:{type:e};t.constructor.schema[s]=r}}const F=[];function M(e){F.push(e)}export{c as Entity,v as Query,R as System,r as Type,C as World,M as component,F as componentTypes,T as prop}; | ||
const e=new TextEncoder,t=new TextDecoder;function s(e){throw new Error(`Component is not writable; use entity.write(${e.type.name}) to acquire a writable version`)}class i{defaultValue;constructor(e){this.defaultValue=e}static boolean;static uint8;static int8;static uint16;static int16;static uint32;static int32;static float32;static float64;static staticString;static dynamicString;static ref;static object;static weakObject}class r extends i{NumberArray;constructor(e){super(0),this.NumberArray=e}defineElastic(e,t){let i,r;t.updateBuffer=()=>{const s=e.capacity*this.NumberArray.BYTES_PER_ELEMENT,n=t.buffer?.byteLength!==s;(n||t.buffer!==i)&&(i=n?new SharedArrayBuffer(s):t.buffer,r=new this.NumberArray(i),n&&t.buffer&&r.set(new this.NumberArray(t.buffer)),t.buffer=i)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>r[e.index],set(t){r[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>r[e.index],set(t){s(e)}})}defineFixed(e,t){const i=e.capacity*this.NumberArray.BYTES_PER_ELEMENT,r=new SharedArrayBuffer(i),n=new this.NumberArray(r);t.buffer=r,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>n[e.index],set(t){n[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>n[e.index],set(t){s(e)}})}}class n extends i{choices;choicesIndex=new Map;TypedArray;constructor(e){if(super(e[0]),this.choices=e,!e?.length)throw new Error("No choices specified for Type.staticString");e.length<256?this.TypedArray=Uint8Array:e.length<65536?this.TypedArray=Uint16Array:this.TypedArray=Uint32Array;for(let t=0;t<e.length;t++)this.choicesIndex.set(e[t],t)}defineElastic(e,t){let i,r;const n=this.choices,a=this.choicesIndex;t.updateBuffer=()=>{const s=e.capacity*this.TypedArray.BYTES_PER_ELEMENT,n=t.buffer?.byteLength!==s;(n||t.buffer!==i)&&(i=n?new SharedArrayBuffer(s):t.buffer,r=new this.TypedArray(i),n&&t.buffer&&r.set(new this.TypedArray(t.buffer)),t.buffer=i)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=r[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){const s=a.get(t);if(void 0===s)throw new Error(`Static string not in set: "${t}"`);r[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=r[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){s(e)}})}defineFixed(e,t){const i=this.choices,r=this.choicesIndex,n=e.capacity*this.TypedArray.BYTES_PER_ELEMENT,a=new SharedArrayBuffer(n),o=new this.TypedArray(a);t.buffer=a,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=o[e.index],s=i[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){const s=r.get(t);if(void 0===s)throw new Error(`Static string not in set: "${t}"`);o[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=o[e.index],s=i[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){s(e)}})}}class a extends i{maxUtf8Length;lengthsStride;bytesStride;constructor(e){super(""),this.maxUtf8Length=e+e%2,this.lengthsStride=e/2+1,this.bytesStride=this.maxUtf8Length+2}defineElastic(i,r){let n,a,o;const h=this.maxUtf8Length,d=this.lengthsStride,c=this.bytesStride;r.updateBuffer=()=>{const e=i.capacity*(this.maxUtf8Length+Uint16Array.BYTES_PER_ELEMENT),t=r.buffer?.byteLength!==e;(t||r.buffer!==n)&&(n=t?new SharedArrayBuffer(e):r.buffer,a=new Uint16Array(n),o=new Uint8Array(n),t&&r.buffer&&o.set(new Uint8Array(r.buffer)),r.buffer=n)},r.updateBuffer(),Object.defineProperty(i.writableInstance,r.name,{enumerable:!0,configurable:!0,get(){const e=a[i.index*d];return t.decode(new Uint8Array(o.buffer,i.index*c+2,e))},set(t){const s=e.encode(t);if(s.byteLength>h)throw new Error(`Dynamic string length > ${h} after encoding: ${t}`);a[i.index*d]=s.byteLength,o.set(s,i.index*c+2)}}),Object.defineProperty(i.readonlyInstance,r.name,{enumerable:!0,configurable:!0,get(){const e=a[i.index*d];return t.decode(new Uint8Array(o.buffer,i.index*c+2,e))},set(e){s(i)}})}defineFixed(i,r){const n=this.maxUtf8Length,a=this.lengthsStride,o=this.bytesStride,h=i.capacity*(this.maxUtf8Length+Uint16Array.BYTES_PER_ELEMENT),d=new SharedArrayBuffer(h),c=new Uint16Array(d),l=new Uint8Array(d);r.buffer=d,Object.defineProperty(i.writableInstance,r.name,{enumerable:!0,configurable:!0,get(){const e=c[i.index*a];return t.decode(new Uint8Array(l.buffer,i.index*o+2,e))},set(t){const s=e.encode(t);if(s.byteLength>n)throw new Error(`Dynamic string length > ${n} after encoding: ${t}`);c[i.index*a]=s.byteLength,l.set(s,i.index*o+2)}}),Object.defineProperty(i.readonlyInstance,r.name,{enumerable:!0,configurable:!0,get(){const e=c[i.index*a];return t.decode(new Uint8Array(l.buffer,i.index*o+2,e))},set(e){s(i)}})}}i.boolean=new class extends i{constructor(){super(!1)}defineElastic(e,t){let i,r;t.updateBuffer=()=>{const s=t.buffer?.byteLength!==e.capacity;(s||t.buffer!==i)&&(i=s?new SharedArrayBuffer(e.capacity):t.buffer,r=new Uint8Array(i),s&&t.buffer&&r.set(new Uint8Array(t.buffer)),t.buffer=i)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(r[e.index]),set(t){r[e.index]=t?1:0}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(r[e.index]),set(t){s(e)}})}defineFixed(e,t){const i=new SharedArrayBuffer(e.capacity),r=new Uint8Array(i);t.buffer=i,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(r[e.index]),set(t){r[e.index]=t?1:0}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(r[e.index]),set(t){s(e)}})}},i.uint8=new r(Uint8Array),i.int8=new r(Int8Array),i.uint16=new r(Uint16Array),i.int16=new r(Int16Array),i.uint32=new r(Uint32Array),i.int32=new r(Int32Array),i.float32=new r(Float32Array),i.float64=new r(Float64Array),i.staticString=e=>new n(e),i.dynamicString=e=>new a(e),i.ref=new class extends i{constructor(){super(null)}defineElastic(e,t){let i,r;t.updateBuffer=()=>{const s=e.capacity*Int32Array.BYTES_PER_ELEMENT,n=t.buffer?.byteLength!==s;(n||t.buffer!==i)&&(i=n?new SharedArrayBuffer(s):t.buffer,r=new Int32Array(i),n&&t.buffer&&r.set(new Int32Array(t.buffer)),t.buffer=i)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=r[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){const s=r[e.index],i=t?.__id??-1;s!==i&&(r[e.index]=i)}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=r[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){s(e)}})}defineFixed(e,t){const i=e.capacity*Int32Array.BYTES_PER_ELEMENT,r=new SharedArrayBuffer(i),n=new Int32Array(r);t.buffer=r,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=n[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){const s=n[e.index],i=t?.__id??-1;s!==i&&(n[e.index]=i)}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=n[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){s(e)}})}},i.object=new class extends i{constructor(){super(void 0)}defineElastic(e,t){const i=[];t.localBuffer=i,t.updateBuffer=()=>{},Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){i[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){s(e)}})}defineFixed(e,t){const i=new Array(e.capacity);t.localBuffer=i,t.updateBuffer=()=>{},Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){i[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){s(e)}})}},i.weakObject=new class extends i{finalizers;constructor(){super(void 0)}defineElastic(e,t){const i=[];t.localBuffer=i,t.updateBuffer=()=>{};const r=this.initFinalizers(e);Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return null==t?t:t.deref()},set(s){if(null!=s){const i=new WeakRef(s);r?.register(s,{type:e.type,field:t,weakRef:i,id:e.entityId,index:e.index}),s=i}i[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return null==t?t:t.deref()},set(t){s(e)}})}defineFixed(e,t){const i=new Array(e.capacity);t.localBuffer=i,t.updateBuffer=()=>{};const r=this.initFinalizers(e);Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return null==t?t:t.deref()},set(s){if(null!=s){const i=new WeakRef(s);r?.register(s,{type:e.type,field:t,weakRef:i,id:e.entityId,index:e.index}),s=i}i[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return null==t?t:t.deref()},set(t){s(e)}})}initFinalizers(e){if(!e.trackedWrites)return;if(this.finalizers)return this.finalizers;const t=e.dispatcher;return t.writeLog&&"undefined"!=typeof FinalizationRegistry?(this.finalizers=new FinalizationRegistry((({type:e,field:s,weakRef:i,id:r,index:n})=>{s.localBuffer?.[n]===i&&t.registry.trackWrite(r,e)})),this.finalizers):void 0}};class o{type;fields;dispatcher;capacity;readonlyInstance;writableInstance;flagOffset;flagMask;trackedWrites;entityId=0;index=0;constructor(e,t,s,i){this.type=e,this.fields=t,this.dispatcher=s,this.capacity=i,this.readonlyInstance=new e,this.writableInstance=new e,this.flagOffset=e.id>>5,this.flagMask=1<<(31&e.id)}}class h{maxEntities;binding;fields;elastic;index;spares;constructor(e,t,s,i){this.maxEntities=e,this.binding=t,this.fields=s,this.elastic=i,this.growSpares(),this.growCapacity()}acquireIndex(e){let t=this.index[e];if(-1===t){if(this.spares[3]>0)t=this.spares[4+--this.spares[3]];else{if(this.spares[1]===this.spares[2]){if(!this.elastic)throw new Error(`Storage exhausted for component ${this.binding.type.name}; raise its capacity above ${this.binding.capacity}`);this.binding.capacity=Math.min(this.maxEntities,2*this.binding.capacity),this.growCapacity()}t=this.spares[1]++}this.index[e]=t}return t}releaseIndex(e){if(-1===this.index[e])throw new Error(`Internal error, index for entity ${e} not allocated`);this.spares[3]===this.spares.length-4&&this.growSpares(),this.spares[4+this.spares[3]++]=this.index[e],this.index[e]=-1}growCapacity(){this.binding.dispatcher.stats.for(this.binding.type).capacity=this.binding.capacity;const e=this.ArrayType,t=e.BYTES_PER_ELEMENT!==this.spares?.[0];if(!this.index||t){const t=new e(new SharedArrayBuffer(this.maxEntities*e.BYTES_PER_ELEMENT));this.index?t.set(this.index):t.fill(-1),this.index=t}if(this.spares&&t){const t=new e(new SharedArrayBuffer(this.spares.length*e.BYTES_PER_ELEMENT));t.set(this.spares),t[0]=e.BYTES_PER_ELEMENT,this.spares=t}if(this.spares[2]=this.binding.capacity,this.elastic)for(const e of this.fields)e.updateBuffer()}growSpares(){const e=this.ArrayType,t=this.spares?Math.min(this.maxEntities,2*(this.spares.length-4)):8,s=new e(new SharedArrayBuffer((4+t)*e.BYTES_PER_ELEMENT));this.spares?s.set(this.spares):(s[0]=e.BYTES_PER_ELEMENT,s[2]=this.binding.capacity),this.spares=s}get ArrayType(){const e=this.binding.capacity;return e<=127?Int8Array:e<=32767?Int16Array:Int32Array}}function d(e,t,s){const r=t.options?.storage??s.defaultComponentStorage,n="sparse"===r?s.maxEntities:Math.min(s.maxEntities,t.options?.capacity??0),a=t.options?.initialCapacity??8;if(void 0!==t.options?.capacity){if("sparse"===r)throw new Error(`Component type ${t.name} cannot combine custom capacity with sparse storage`);if(t.options.capacity<=0)throw new Error(`Component type ${t.name} capacity option must be great than zero: got ${n}`);if(void 0!==t.options.initialCapacity)throw new Error(`Component type ${t.name} cannot have both capacity and initialCapacity options`)}if(("undefined"==typeof process||"test"!==process.env.NODE_ENV)&&t.__bind)throw new Error(`Component type ${t.name} is already in use in another world`);t.id=e;const d=new o(t,function(e){const t=e.schema,s=[];for(const e in t){const r=t[e];let n;n=r instanceof i?{name:e,default:r.defaultValue,type:r}:Object.assign({name:e,default:r.type.defaultValue},r),s.push(n)}return s}(t),s,n||a);t.__binding=d;for(const e of d.fields)n?e.type.defineFixed(d,e):e.type.defineElastic(d,e);switch(r){case"sparse":s.stats.for(t).capacity=n,t.__bind=(e,t)=>(d.entityId=e,d.index=e,t?d.writableInstance:d.readonlyInstance),t.__create=e=>(d.entityId=e,d.index=e,d.writableInstance);break;case"packed":{const e=new h(s.maxEntities,d,d.fields,!n);t.__bind=(s,i)=>{if(d.entityId=s,d.index=e.index[s],-1===d.index)throw new Error(`Attempt to bind unacquired entity ${s} to ${t.name}`);return i?d.writableInstance:d.readonlyInstance},t.__create=t=>(d.entityId=t,d.index=e.acquireIndex(t),d.writableInstance),t.__delete=t=>{e.releaseIndex(t)};break}case"compact":throw new Error("Not yet implemented");default:throw new Error(`Invalid storage type "${r}`)}}const c=Object.freeze({});class l{__registry;__id;joined;constructor(e){this.__registry=e}__reset(e){this.__id=e,this.joined=c}add(e,t){if(this.__checkMask(e,!0),this.__registry.hasFlag(this.__id,e))throw new Error(`Entity already has a ${e.name} component`);return this.__registry.setFlag(this.__id,e),this.__registry.dispatcher.stats.for(e).numEntities+=1,function(e,t,s){if(void 0!==s)for(const t in s)if(!e.schema?.[t])throw new Error(`Property ${t} not defined for component ${e.name}`);const i=e.__create(t);for(const t of e.__binding.fields)i[t.name]=s?.[t.name]??t.default}(e,this.__id,t),this}addAll(...e){for(let t=0;t<e.length;t++){const s=e[t];if("function"!=typeof s)throw new Error(`Bad arguments to bulk add: expected component type, got: ${s}`);let i=e[t+1];"function"==typeof i?i=void 0:t++,this.add(s,i)}return this}remove(e){if(!this.has(e))throw new Error(`Entity doesn't have a ${e.name} component`);this.__remove(e)}removeAll(...e){for(const t of e)this.remove(t)}has(e,t=!1){return this.__checkMask(e,!1),this.__registry.hasFlag(this.__id,e,t)}read(e){if(!this.has(e))throw new Error(`Entity doesn't have a ${e.name} component`);return e.__bind(this.__id,!1)}readRecentlyRemoved(e){if(!this.has(e,!0))throw new Error(`Entity doesn't have a ${e.name} component`);return e.__bind(this.__id,!1)}write(e){if(!this.has(e,!0))throw new Error(`Entity doesn't have a ${e.name} component`);return e.__binding.trackedWrites&&this.__registry.trackWrite(this.__id,e),e.__bind(this.__id,!0)}delete(){for(const e of this.__registry.types)this.__registry.hasFlag(this.__id,e)&&(this.__checkMask(e,!0),this.__remove(e));this.__registry.queueDeletion(this.__id),this.__wipeInboundRefs()}__remove(e){this.__deindexOutboundRefs(e),e.__delete&&this.__registry.queueRemoval(this.__id,e),this.__registry.clearFlag(this.__id,e),this.__registry.dispatcher.stats.for(e).numEntities-=1}__deindexOutboundRefs(e){const t=e.__binding.fields;if(t.some((e=>e.type===i.ref))){const s=this.write(e);for(const e of t)e.type===i.ref&&(s[e.name]=null)}}__wipeInboundRefs(){}__checkMask(e,t){const s=this.__registry.executingSystem?.rwMasks,i=t?s?.write:s?.read;if(i&&!this.__registry.maskHasFlag(i,e))throw new Error(`System didn't mark component ${e.name} as ${t?"writable":"readable"}`)}}class u{size;bytes;constructor(e){this.size=e,this.bytes=new Uint32Array(Math.ceil(e/32))}get(e){if(e<0||e>=this.size)throw new Error(`Bit index out of bounds: ${e}`);return 0!=(this.bytes[e>>>5]&1<<(31&e))}set(e){if(e<0||e>=this.size)throw new Error(`Bit index out of bounds: ${e}`);this.bytes[e>>>5]|=1<<(31&e)}unset(e){if(e<0||e>=this.size)throw new Error(`Bit index out of bounds: ${e}`);this.bytes[e>>>5]&=~(1<<(31&e))}clear(){this.bytes.fill(0)}}const f=[];class m{maxEntries;configParamName;data;corral;constructor(e,t){this.maxEntries=e,this.configParamName=t;const s=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.data=new Uint32Array(s);const i=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.corral=new Uint32Array(i)}push(e){const t=this.corral[0];t>=this.maxEntries&&this.throwCapacityExceeded(),t&&this.corral[t]===e||(this.corral[t+2]=e,this.corral[0]+=1)}commit(){const e=this.corral[0];if(!e)return;let t=this.data[0];const s=Math.min(e,this.maxEntries-t);for(this.data.set(this.corral.subarray(2,s+2),t+2),s<e&&this.data.set(this.corral.subarray(s+2,e+2),2),t+=e;t>=this.maxEntries;)t-=this.maxEntries,this.data[1]+=1;this.data[0]=t,this.corral[0]=0,this.corral[1]+=1}createPointer(e){return e?(e.index=this.data[0],e.generation=this.data[1],e.corralIndex=this.corral[0],e.corralGeneration=this.corral[1],e):{index:this.data[0],generation:this.data[1],corralIndex:this.corral[0],corralGeneration:this.corral[1]}}hasUpdatesSince(e){return this.checkPointer(e),!(e.index===this.data[0]&&e.generation===this.data[1]&&(e.corralGeneration===this.corral[1]?e.corralIndex===this.corral[0]:0===this.corral[0]))}processSince(e,t){this.checkPointers(e,t);let s=f;const i=t?.index??this.data[0],r=t?.generation??this.data[1];if(e.generation===r)if(e.index<i)s=[this.data,e.index+2,i+2],e.index=i;else{const t=this.corral[0],i=this.corral[1];(e.corralGeneration===i?e.corralIndex<t:t)&&(s=[this.corral,e.corralIndex+2,t+2],e.corralIndex=t,e.corralGeneration=i)}else s=[this.data,e.index+2,this.data.length],e.index=0,e.generation=r;return s}countSince(e,t){if(this.checkPointers(e,t),this.corral[0])throw new Error("Internal error, should commit log before counting");const s=e.index,i=t?.index??this.data[0],r=t?.generation??this.data[1];return e.index=i,e.generation=r,s===i&&e.generation===r?0:s<i?i-s:this.maxEntries-(s-i)}checkPointers(e,t){if(this.checkPointer(e),t&&(this.checkPointer(t),e.index>t.index&&e.generation>=t.generation))throw new RangeError("Internal error, start pointer exceeds end pointer")}checkPointer(e){const t=this.data[0];let s=e.generation;if(e.index===t?s+1<this.data[1]&&this.throwCapacityExceeded():(e.index>t&&(s+=1),s!==this.data[1]&&this.throwCapacityExceeded()),e.corralGeneration>this.corral[1])throw new Error("Internal error, pointer corral generation older than corral");if(e.corralGeneration===this.corral[1]&&e.corralIndex>this.corral[0])throw new Error("Internal error, pointer past end of log corral area")}throwCapacityExceeded(){throw new Error(`Log capacity exceeded, please raise ${this.configParamName} above ${this.maxEntries}`)}}class g{maxItems;configParamName;data;constructor(e,t){this.maxItems=e,this.configParamName=t,this.data=new Uint32Array(new SharedArrayBuffer((e+1)*Uint32Array.BYTES_PER_ELEMENT))}get length(){return this.data[0]}take(){const e=this.data[0]--;if(e<=0)throw new RangeError(`Pool capacity exceeded, please raise ${this.configParamName} above ${this.maxItems}`);return this.data[e]}refill(e){if(!e.length)return;const t=this.length,s=t+e.length;if(s>this.maxItems)throw new Error("Internal error, refill exceeded pool capacity");this.data.set(e,t+1),this.data[0]=s}fillWithDescendingIntegers(e){const t=this.length;for(let s=this.data.length-1;s>t;s--)this.data[s]=e++;this.data[0]=this.data.length-1}}class p{pool;entities=[];constructor(e){this.pool=e}add(e){this.entities.push(this.pool.borrowTemporarily(e))}clear(){this.entities.length&&this.entities.splice(0,1/0)}}class y{pool;entities=[];lookupTable;constructor(e,t){this.pool=e,this.lookupTable=new Int32Array(t),this.lookupTable.fill(-1)}add(e){const t=this.entities.push(this.pool.borrow(e))-1;this.lookupTable[e]=t}remove(e){const t=this.lookupTable[e];if(t<0)throw new Error("Internal error, entity not in list");this.pool.return(e),this.lookupTable[e]=-1;const s=this.entities.pop();t<this.entities.length&&(this.entities[t]=s,this.lookupTable[s.__id]=t)}has(e){return this.lookupTable[e]>=0}clear(){throw new Error("Internal error, trying to clear persistent entity list")}}var _;!function(e){e[e.all=1]="all",e[e.added=2]="added",e[e.removed=4]="removed",e[e.changed=8]="changed",e[e.addedOrChanged=16]="addedOrChanged",e[e.changedOrRemoved=32]="changedOrRemoved",e[e.addedChangedOrRemoved=64]="addedChangedOrRemoved"}(_||(_={}));const w=_.added|_.removed|_.changed|_.addedOrChanged|_.changedOrRemoved|_.addedChangedOrRemoved,b=_.changed|_.addedOrChanged|_.changedOrRemoved|_.addedChangedOrRemoved;class x{query;system;results={};flavors=0;__withMask;__withoutMask;__trackMask;__refMask;hasTransientResults;hasChangedResults;currentEntities;changedEntities;constructor(e,t){this.query=e,this.system=t,e.__results=this.results,e.__systemName=t.name}complete(){const e=this.system.dispatcher;if(this.hasTransientResults=Boolean(this.flavors&w),this.hasChangedResults=Boolean(this.flavors&b),this.hasChangedResults&&!this.__trackMask)throw new Error("Query for changed entities must track at least one component");this.flavors&_.all?this.results.all=new y(e.registry.pool,e.maxEntities):this.currentEntities=new u(e.maxEntities),this.hasTransientResults&&this.allocateTransientResultLists(),this.flavors&&this.system.shapeQueries.push(this),this.hasChangedResults&&(this.changedEntities=new u(e.maxEntities),this.system.writeQueries.push(this))}allocateTransientResultLists(){this.flavors&_.added&&this.allocateResult("added"),this.flavors&_.removed&&this.allocateResult("removed"),this.flavors&_.changed&&this.allocateResult("changed"),this.flavors&_.addedOrChanged&&this.allocateResult("addedOrChanged"),this.flavors&_.changedOrRemoved&&this.allocateResult("changedOrRemoved"),this.flavors&_.addedChangedOrRemoved&&this.allocateResult("addedChangedOrRemoved")}allocateResult(e){const t=this.system.dispatcher;this.results[e]=new p(t.registry.pool)}clearTransientResults(){this.hasTransientResults&&(this.results.added?.clear(),this.results.removed?.clear(),this.results.changed?.clear(),this.results.addedOrChanged?.clear(),this.results.changedOrRemoved?.clear(),this.results.addedChangedOrRemoved?.clear(),this.changedEntities?.clear())}handleShapeUpdate(e){const t=this.system.dispatcher.registry,s=this.results.all?.has(e)??this.currentEntities.get(e),i=t.matchShape(e,this.__withMask,this.__withoutMask);i&&!s?(this.currentEntities?.set(e),this.results.all?.add(e),this.results.added?.add(e),this.results.addedOrChanged?.add(e),this.results.addedChangedOrRemoved?.add(e)):!i&&s&&(this.currentEntities?.unset(e),this.results.all?.remove(e),this.results.removed?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}handleWrite(e,t,s){!this.changedEntities.get(e)&&(this.__trackMask[t]??0)&s&&(this.changedEntities.set(e),this.results.changed?.add(e),this.results.addedOrChanged?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}}class E{__callback;__userQuery;__query;__system;__lastTypes;constructor(e,t){this.__callback=e,this.__userQuery=t}__build(e){try{this.__system=e,this.__query=new x(this.__userQuery,e),this.__callback(this),this.__query.complete()}catch(t){throw t.message=`Failed to build query in system ${e.name}: ${t.message}`,t}}get and(){return this}get but(){return this}get also(){return this}get all(){return this.__query.flavors|=_.all,this}get added(){return this.__query.flavors|=_.added,this}get removed(){return this.__query.flavors|=_.removed,this}get changed(){return this.__query.flavors|=_.changed,this}get addedOrChanged(){return this.__query.flavors|=_.addedOrChanged,this}get changedOrRemoved(){return this.__query.flavors|=_.changedOrRemoved,this}get addedChangedOrRemoved(){return this.__query.flavors|=_.addedChangedOrRemoved,this}with(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withMask"),this}without(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withoutMask",e),this}using(...e){return this.set(this.__system.rwMasks.read,e),this}get track(){this.set("__trackMask");for(const e of this.__lastTypes)e.__binding.trackedWrites=!0;return this}get read(){return this}get write(){return this.set(this.__system.rwMasks.write),this}set(e,t,s){if(e){if(t||(t=this.__lastTypes),!t)throw new Error("No component type to apply query modifier to");if(this.__lastTypes=t,"string"==typeof e){if(s&&this.__query[e])throw new Error(`Only one ${s} allowed`);this.__query[e]||(this.__query[e]=[]),e=this.__query[e]}else if(s&&e.some((e=>0!==e)))throw new Error(`Only one ${s} allowed`);for(const s of t)this.__system.dispatcher.registry.extendMaskAndSetFlag(e,s)}}}class v{__results;__systemName;get all(){return this.__checkList("all"),this.__results.all.entities}get added(){return this.__checkList("added"),this.__results.added.entities}get removed(){return this.__checkList("removed"),this.__results.removed.entities}get changed(){return this.__checkList("changed"),this.__results.changed.entities}get addedOrChanged(){return this.__checkList("addedOrChanged"),this.__results.addedOrChanged.entities}get changedOrRemoved(){return this.__checkList("changedOrRemoved"),this.__results.changedOrRemoved.entities}get addedChangedOrRemoved(){return this.__checkList("addedChangedOrRemoved"),this.__results.addedChangedOrRemoved.entities}__checkList(e){if(!this.__results[e])throw new Error(`Query '${e}' not configured, please add .${e} to your query definition in system ${this.__systemName}`)}}class L{__queryBuilders=[];__dispatcher;time;delta;get name(){return this.constructor.name}query(e){const t=new v,s=new E(e,t);if(!this.__queryBuilders)throw new Error(`Attempt to create a new query after world initialized in system ${this.name}`);return this.__queryBuilders.push(s),t}createEntity(...e){return this.__dispatcher.createEntity(e)}}class P{system;dispatcher;rwMasks={read:[],write:[]};shapeQueries=[];writeQueries=[];hasWriteQueries;processedEntities;shapeLogPointer;writeLogPointer;get name(){return this.system.name}constructor(e,t){this.system=e,this.dispatcher=t,e.__dispatcher=t,this.shapeLogPointer=t.shapeLog.createPointer(),this.writeLogPointer=t.writeLog?.createPointer(),this.processedEntities=new u(t.maxEntities);for(const t of e.__queryBuilders)t.__build(this);e.__queryBuilders=null,this.hasWriteQueries=!!this.writeQueries.length}execute(e,t){this.system.time=e,this.system.delta=t,this.runQueries(),this.system.execute()}runQueries(){const e=this.dispatcher.shapeLog.hasUpdatesSince(this.shapeLogPointer),t=this.hasWriteQueries&&this.dispatcher.writeLog.hasUpdatesSince(this.writeLogPointer);if(e||t){this.processedEntities.clear();for(const e of this.shapeQueries)e.clearTransientResults();e&&this.__updateShapeQueries(),t&&this.__updateWriteQueries()}}__updateShapeQueries(){const e=this.dispatcher.shapeLog;let t,s,i;for(;[t,s,i]=e.processSince(this.shapeLogPointer),t;)for(let e=s;e<i;e++){const s=t[e];if(!this.processedEntities.get(s)){this.processedEntities.set(s);for(const e of this.shapeQueries)e.handleShapeUpdate(s)}}}__updateWriteQueries(){const e=this.dispatcher.writeLog;let t,s,i;for(;[t,s,i]=e.processSince(this.writeLogPointer),t;)for(let e=s;e<i;e++){const s=t[e],i=4194303&s;if(!this.processedEntities.get(i)){const e=s>>>22;for(const t of this.writeQueries)t.handleWrite(i,e>>5,1<<(31&e))}}}}class S{registry;borrowed;borrowCounts;spares=[];temporarilyBorrowedIds=[];constructor(e,t){this.registry=e,this.borrowed=Array.from({length:t}),this.borrowCounts=new Int32Array(t)}borrow(e){this.borrowCounts[e]+=1;let t=this.borrowed[e];return t||(t=this.borrowed[e]=this.spares.pop()??new l(this.registry),t.__reset(e)),t}borrowTemporarily(e){const t=this.borrow(e);return this.temporarilyBorrowedIds.push(e),t}returnTemporaryBorrows(){for(const e of this.temporarilyBorrowedIds)this.return(e);this.temporarilyBorrowedIds.splice(0,1/0)}return(e){if(!this.borrowCounts[e])throw new Error("Internal error, returning entity with no borrows");--this.borrowCounts[e]<=0&&(this.spares.push(this.borrowed[e]),this.borrowed[e]=void 0)}}class I{types;dispatcher;stride;shapes;staleShapes;entityIdPool;pool;executingSystem;deletionLog;prevDeletionPointer;oldDeletionPointer;removalLog;prevRemovalPointer;oldRemovalPointer;constructor(e,t,s,i,r){this.types=i,this.dispatcher=r;let n=0;for(const e of i)d(n++,e,this.dispatcher);this.stride=Math.ceil(i.length/32);const a=e*this.stride*4;this.shapes=new Uint32Array(new SharedArrayBuffer(a)),this.staleShapes=new Uint32Array(new SharedArrayBuffer(a)),this.entityIdPool=new g(e,"maxEntities"),this.entityIdPool.fillWithDescendingIntegers(0),this.pool=new S(this,e),this.deletionLog=new m(t,"maxLimboEntities"),this.prevDeletionPointer=this.deletionLog.createPointer(),this.oldDeletionPointer=this.deletionLog.createPointer(),this.removalLog=new m(s,"maxLimboComponents"),this.prevRemovalPointer=this.removalLog.createPointer(),this.oldRemovalPointer=this.removalLog.createPointer()}createEntity(e){const t=this.entityIdPool.take();this.shapes.fill(0,t*this.stride,(t+1)*this.stride);const s=this.pool.borrowTemporarily(t);return e&&s.addAll(...e),this.dispatcher.stats.numEntities+=1,s}queueDeletion(e){this.deletionLog.push(e)}queueRemoval(e,t){this.removalLog.push(e|t.id<<22)}flush(){this.pool.returnTemporaryBorrows(),this.deletionLog.commit(),this.removalLog.commit()}processEndOfFrame(){this.processDeletionLog(),this.processRemovalLog()}processDeletionLog(){this.deletionLog.commit();let e,t,s,i=0;for(;[e,t,s]=this.deletionLog.processSince(this.oldDeletionPointer,this.prevDeletionPointer),e;){const r=e.subarray(t,s);this.entityIdPool.refill(r),i+=r.length}this.dispatcher.stats.numEntities-=i,this.dispatcher.stats.maxLimboEntities=i,this.deletionLog.createPointer(this.prevDeletionPointer)}processRemovalLog(){this.removalLog.commit();let e,t,s,i=0;for(;[e,t,s]=this.removalLog.processSince(this.oldRemovalPointer,this.prevRemovalPointer),e;){for(let i=t;i<s;i++){const t=e[i],s=4194303&t,r=t>>>22,n=this.types[r],a=s*this.stride+n.__binding.flagOffset,o=n.__binding.flagMask;0==(this.shapes[a]&o)&&(this.staleShapes[a]&=~o,n.__delete(s))}i+=s-t}this.dispatcher.stats.maxLimboComponents=i,this.removalLog.createPointer(this.prevRemovalPointer)}extendMaskAndSetFlag(e,t){const s=t.__binding.flagOffset;s>=e.length&&(e.length=s+1,e.fill(0,e.length,s)),e[s]|=t.__binding.flagMask}maskHasFlag(e,t){return 0!=((e?.[t.__binding.flagOffset]??0)&t.__binding.flagMask)}hasFlag(e,t,s=!1){const i=e*this.stride+t.__binding.flagOffset,r=t.__binding.flagMask;return 0!=(this.shapes[i]&r)||!(!s||0==(this.staleShapes[i]&r))}setFlag(e,t){const s=e*this.stride+t.__binding.flagOffset,i=t.__binding.flagMask;this.shapes[s]|=i,this.staleShapes[s]|=i,this.dispatcher.shapeLog.push(e)}clearFlag(e,t){this.shapes[e*this.stride+t.__binding.flagOffset]&=~t.__binding.flagMask,this.dispatcher.shapeLog.push(e)}trackWrite(e,t){this.dispatcher.writeLog.push(e|t.id<<22)}matchShape(e,t,s){const i=e*this.stride;if(t)for(let e=0;e<t.length;e++){const s=t[e];if((this.shapes[i+e]&s)!==s)return!1}if(s)for(let e=0;e<s.length;e++){const t=s[e];if(0!=(this.shapes[i+e]&t))return!1}return!0}}class R{_numEntities=0;maxEntities=0;capacity=0;get numEntities(){return this._numEntities}set numEntities(e){this._numEntities=e,e>this.maxEntities&&(this.maxEntities=e)}toString(){return`${this.numEntities.toLocaleString()} of ${this.maxEntities.toLocaleString()} peak (capacity ${this.capacity.toLocaleString()})`}}class k{frames=0;_numEntities=0;maxEntities=0;_maxLimboEntities=0;_maxLimboComponents=0;_numRefs=0;maxRefs=0;_maxShapeChangesPerFrame=0;_maxWritesPerFrame=0;components=Object.create(null);get numEntities(){return this._numEntities}set numEntities(e){this._numEntities=e,e>this.maxEntities&&(this.maxEntities=e)}get maxLimboEntities(){return this._maxLimboEntities}set maxLimboEntities(e){e>this._maxLimboEntities&&(this._maxLimboEntities=e)}get maxLimboComponents(){return this._maxLimboComponents}set maxLimboComponents(e){e>this._maxLimboComponents&&(this._maxLimboComponents=e)}get numRefs(){return this._numRefs}set numRefs(e){this._numRefs=e,e>this.maxRefs&&(this.maxRefs=e)}get maxShapeChangesPerFrame(){return this._maxShapeChangesPerFrame}set maxShapeChangesPerFrame(e){e>this._maxShapeChangesPerFrame&&(this._maxShapeChangesPerFrame=e)}get maxWritesPerFrame(){return this._maxWritesPerFrame}set maxWritesPerFrame(e){e>this._maxWritesPerFrame&&(this._maxWritesPerFrame=e)}for(e){return this.components[e.name]=this.components[e.name]??new R}toString(){return`World stats:\n frames: ${this.frames.toLocaleString()}\n entities: ${this.numEntities.toLocaleString()} of ${this.maxEntities.toLocaleString()} max (${this.maxLimboEntities.toLocaleString()} limbo max)\n refs: ${this.numRefs.toLocaleString()} of ${this.maxRefs.toLocaleString()} max\n logs: ${this.maxShapeChangesPerFrame.toLocaleString()} shape changes/frame max, ${this.maxWritesPerFrame.toLocaleString()} writes/frame max`}}const A="undefined"!=typeof window&&void 0!==window.performance?performance.now.bind(performance):Date.now.bind(Date);class O extends L{__callback;execute(){this.__callback(this)}}class T{maxEntities;defaultComponentStorage;registry;systems;lastTime=A()/1e3;executing;shapeLog;writeLog;shapeLogFramePointer;writeLogFramePointer;stats;userCallbackSystem;callbackSystem;constructor({defs:e,maxEntities:t=1e4,maxLimboEntities:s=Math.ceil(t/5),maxLimboComponents:i=Math.ceil(t/5),maxRefs:r=t,maxShapeChangesPerFrame:n=2*t,maxWritesPerFrame:a=4*t,defaultComponentStorage:o="sparse"}){if(t>4194303)throw new Error("maxEntities too high, the limit is 4194303");const{componentTypes:h,systemTypes:d}=this.splitDefs(e);if(h.length>1024)throw new Error("Too many component types, the limit is 1024");this.stats=new k,this.maxEntities=t,this.defaultComponentStorage=o,this.shapeLog=new m(n,"maxShapeChangesPerFrame"),this.shapeLogFramePointer=this.shapeLog.createPointer(),this.registry=new I(t,s,i,h.flat(1/0),this),this.systems=this.normalizeAndInitSystems(d),this.systems.some((e=>e.hasWriteQueries))&&(this.writeLog=new m(a,"maxWritesPerFrame"),this.writeLogFramePointer=this.writeLog.createPointer()),this.userCallbackSystem=new O,this.callbackSystem=new P(this.userCallbackSystem,this)}normalizeAndInitSystems(e){const t=[],s=e.flat(1/0);for(let e=0;e<s.length;e++){const i=new s[e],r=s[e+1];r&&"function"!=typeof r&&(Object.assign(i,r),e++),t.push(new P(i,this))}return t}splitDefs(e){const t=[],s=[];let i=!1;for(const r of e.flat(1/0))if("function"==typeof r)i=!r.schema,(i?s:t).push(r);else{if(!i)throw new Error("Unexpected value in world defs: "+r);s.push(r),i=!1}return{componentTypes:t,systemTypes:s}}execute(e,t,s){if(this.executing)throw new Error("Recursive system execution not allowed");this.executing=!0,void 0===e&&(e=A()/1e3),void 0===t&&(t=e-this.lastTime),this.lastTime=e;for(const i of s??this.systems)this.registry.executingSystem=i,i.execute(e,t),this.flush();this.registry.executingSystem=void 0,this.registry.processEndOfFrame(),this.executing=!1,this.gatherFrameStats()}executeFunction(e){if(this.executing)throw new Error("Ad hoc function execution not allowed while world is executing");this.executing=!0,this.userCallbackSystem.__callback=e,this.callbackSystem.execute(0,0),this.flush(),this.registry.processEndOfFrame(),this.executing=!1,this.gatherFrameStats()}gatherFrameStats(){this.stats.frames+=1,this.stats.maxShapeChangesPerFrame=this.shapeLog.countSince(this.shapeLogFramePointer),this.stats.maxWritesPerFrame=this.writeLog?.countSince(this.writeLogFramePointer)??0}flush(){this.registry.flush(),this.shapeLog.commit(),this.writeLog?.commit()}createEntity(e){const t=this.registry.createEntity(e);return this.executing||this.flush(),t}}class C{__dispatcher;constructor(e){this.__dispatcher=new T(e)}build(e){this.__dispatcher.executeFunction(e)}createEntity(...e){this.__dispatcher.createEntity(e)}execute(e,t){this.__dispatcher.execute(e,t)}get stats(){return this.__dispatcher.stats}}function B(e){return function(t,s){t.constructor.schema||(t.constructor.schema={});const i="type"in e?e:{type:e};t.constructor.schema[s]=i}}const F=[];function $(e){if("function"!=typeof e)return t=>{t.options=e,F.push(t)};F.push(e)}export{l as Entity,v as Query,L as System,i as Type,C as World,$ as component,F as componentTypes,B as prop}; | ||
//# sourceMappingURL=index.min.js.map |
@@ -1,38 +0,1 @@ | ||
declare class Indexer { | ||
private readonly maxRefs; | ||
private readonly buffer; | ||
private readonly index; | ||
private numRefs; | ||
constructor(maxRefs: number); | ||
/** | ||
* Inserts a new reference entry into the index, but fails if this exact pair is already indexed. | ||
*/ | ||
insert(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Removes a reference entry from the index, failing if it's missing. | ||
*/ | ||
remove(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Returns an iterable over the IDs of all entities that refer to the given one. | ||
*/ | ||
iterateReferrers(referenceId: EntityId): Iterable<EntityId>; | ||
/** | ||
* Finds the entry that matches referencedId:referrerId exactly. If not found, finds the first | ||
* entry that would follow referenceId:referrerId. If referrerId is not specified, finds the | ||
* first referenceId:* entry, if any. | ||
*/ | ||
private findIndex; | ||
} | ||
/** | ||
* A fixed but arbitrary size bitset. | ||
*/ | ||
declare class Bitset { | ||
private readonly size; | ||
private readonly bytes; | ||
constructor(size: number); | ||
get(index: number): boolean; | ||
set(index: number): void; | ||
unset(index: number): void; | ||
clear(): void; | ||
} | ||
interface LogPointer { | ||
@@ -215,3 +178,2 @@ index: number; | ||
private processedEntities; | ||
removedEntities: Bitset; | ||
private shapeLogPointer; | ||
@@ -226,10 +188,9 @@ private writeLogPointer; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defs: DefsArray; | ||
declare class ComponentStats { | ||
_numEntities: number; | ||
maxEntities: number; | ||
capacity: number; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
toString(): string; | ||
} | ||
@@ -240,22 +201,40 @@ declare class Stats { | ||
maxEntities: number; | ||
_maxLimboEntities: number; | ||
_maxLimboComponents: number; | ||
_numRefs: number; | ||
maxRefs: number; | ||
_maxShapeChangesPerFrame: number; | ||
_maxWritesPerFrame: number; | ||
components: { | ||
[typeName: string]: ComponentStats; | ||
}; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
_maxLimboEntities: number; | ||
get maxLimboEntities(): number; | ||
set maxLimboEntities(value: number); | ||
_numRefs: number; | ||
maxRefs: number; | ||
get maxLimboComponents(): number; | ||
set maxLimboComponents(value: number); | ||
get numRefs(): number; | ||
set numRefs(value: number); | ||
_maxShapeChangesPerFrame: number; | ||
get maxShapeChangesPerFrame(): number; | ||
set maxShapeChangesPerFrame(value: number); | ||
_maxWritesPerFrame: number; | ||
get maxWritesPerFrame(): number; | ||
set maxWritesPerFrame(value: number); | ||
for(type: ComponentType<any>): ComponentStats; | ||
toString(): string; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
defs: DefsArray; | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxLimboComponents?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defaultComponentStorage?: ComponentStorage; | ||
} | ||
declare class Dispatcher { | ||
readonly maxEntities: number; | ||
readonly indexer: Indexer; | ||
readonly defaultComponentStorage: ComponentStorage; | ||
readonly registry: Registry; | ||
@@ -272,3 +251,3 @@ readonly systems: SystemBox[]; | ||
private readonly callbackSystem; | ||
constructor({ defs, maxEntities, maxLimboEntities, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame }: WorldOptions); | ||
constructor({ defs, maxEntities, maxLimboEntities, maxLimboComponents, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame, defaultComponentStorage }: WorldOptions); | ||
private normalizeAndInitSystems; | ||
@@ -299,2 +278,3 @@ private splitDefs; | ||
private readonly shapes; | ||
private readonly staleShapes; | ||
private readonly entityIdPool; | ||
@@ -306,7 +286,13 @@ readonly pool: EntityPool; | ||
private readonly oldDeletionPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
private readonly removalLog; | ||
private readonly prevRemovalPointer; | ||
private readonly oldRemovalPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, maxLimboComponents: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
createEntity(initialComponents: (ComponentType<any> | any)[]): Entity; | ||
queueDeletion(id: EntityId): void; | ||
queueRemoval(id: EntityId, type: ComponentType<any>): void; | ||
flush(): void; | ||
processEndOfFrame(): void; | ||
private processDeletionLog; | ||
private processRemovalLog; | ||
extendMaskAndSetFlag(mask: number[], type: ComponentType<any>): void; | ||
@@ -350,3 +336,4 @@ maskHasFlag(mask: number[] | undefined, type: ComponentType<any>): boolean; | ||
constructor(defaultValue: JSType); | ||
abstract define<C>(binding: Binding<C>, name: string, buffer?: SharedArrayBuffer): SharedArrayBuffer; | ||
abstract defineElastic(binding: Binding<any>, field: Field<any>): void; | ||
abstract defineFixed(binding: Binding<any>, field: Field<any>): void; | ||
static boolean: Type<boolean>; | ||
@@ -364,2 +351,4 @@ static uint8: Type<number>; | ||
static ref: Type<Entity | null>; | ||
static object: Type<any>; | ||
static weakObject: Type<any>; | ||
} | ||
@@ -373,2 +362,8 @@ interface SchemaDef<JSType> { | ||
} | ||
type ComponentStorage = "sparse" | "packed" | "compact"; | ||
interface ComponentOptions { | ||
storage?: ComponentStorage; | ||
capacity?: number; | ||
initialCapacity?: number; | ||
} | ||
interface Field<JSType> { | ||
@@ -378,2 +373,5 @@ name: string; | ||
default: JSType; | ||
buffer?: SharedArrayBuffer; | ||
localBuffer?: any[]; | ||
updateBuffer?(): void; | ||
} | ||
@@ -383,19 +381,27 @@ interface ComponentType<C> { | ||
schema?: Schema; | ||
maxEntities?: number; | ||
__id?: number; | ||
__flagOffset?: number; | ||
__flagMask?: number; | ||
__trackedWrites?: boolean; | ||
__fields?: Field<any>[]; | ||
options?: ComponentOptions; | ||
/** | ||
* A unique, sequential id number for this component type, assigned automatically by becsy. It | ||
* will stay the same across runs as long as the list of defs used to create the world doesn't | ||
* change. Feel free to use this for your own purposes but don't change it. | ||
*/ | ||
id?: number; | ||
__binding?: Binding<C>; | ||
__bind?(id: EntityId, writable: boolean): C; | ||
__create?(id: EntityId): C; | ||
__delete?(id: EntityId): void; | ||
} | ||
declare class Binding<C> { | ||
readonly type: ComponentType<C>; | ||
readonly fields: Field<any>[]; | ||
readonly dispatcher: Dispatcher; | ||
readonly maxEntities: number; | ||
capacity: number; | ||
readonly readonlyInstance: C; | ||
readonly writableInstance: C; | ||
readonly flagOffset: number; | ||
readonly flagMask: number; | ||
trackedWrites: boolean; | ||
entityId: number; | ||
index: number; | ||
constructor(type: ComponentType<C>, dispatcher: Dispatcher, maxEntities: number); | ||
constructor(type: ComponentType<C>, fields: Field<any>[], dispatcher: Dispatcher, capacity: number); | ||
} | ||
@@ -408,3 +414,3 @@ declare class World { | ||
execute(time?: number, delta?: number): void; | ||
get stats(): any; | ||
get stats(): Stats; | ||
} | ||
@@ -418,3 +424,4 @@ interface PropOptions<JSType> { | ||
declare function component(constructor: ComponentType<any>): void; | ||
declare function component(options: ComponentOptions): (constructor: ComponentType<any>) => void; | ||
export { World, Type, Entity, System, SystemType, componentTypes, component, prop, Query, ComponentType }; | ||
//# sourceMappingURL=index.umd.d.ts.map |
@@ -1,38 +0,1 @@ | ||
declare class Indexer { | ||
private readonly maxRefs; | ||
private readonly buffer; | ||
private readonly index; | ||
private numRefs; | ||
constructor(maxRefs: number); | ||
/** | ||
* Inserts a new reference entry into the index, but fails if this exact pair is already indexed. | ||
*/ | ||
insert(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Removes a reference entry from the index, failing if it's missing. | ||
*/ | ||
remove(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Returns an iterable over the IDs of all entities that refer to the given one. | ||
*/ | ||
iterateReferrers(referenceId: EntityId): Iterable<EntityId>; | ||
/** | ||
* Finds the entry that matches referencedId:referrerId exactly. If not found, finds the first | ||
* entry that would follow referenceId:referrerId. If referrerId is not specified, finds the | ||
* first referenceId:* entry, if any. | ||
*/ | ||
private findIndex; | ||
} | ||
/** | ||
* A fixed but arbitrary size bitset. | ||
*/ | ||
declare class Bitset { | ||
private readonly size; | ||
private readonly bytes; | ||
constructor(size: number); | ||
get(index: number): boolean; | ||
set(index: number): void; | ||
unset(index: number): void; | ||
clear(): void; | ||
} | ||
interface LogPointer { | ||
@@ -215,3 +178,2 @@ index: number; | ||
private processedEntities; | ||
removedEntities: Bitset; | ||
private shapeLogPointer; | ||
@@ -226,10 +188,9 @@ private writeLogPointer; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defs: DefsArray; | ||
declare class ComponentStats { | ||
_numEntities: number; | ||
maxEntities: number; | ||
capacity: number; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
toString(): string; | ||
} | ||
@@ -240,22 +201,40 @@ declare class Stats { | ||
maxEntities: number; | ||
_maxLimboEntities: number; | ||
_maxLimboComponents: number; | ||
_numRefs: number; | ||
maxRefs: number; | ||
_maxShapeChangesPerFrame: number; | ||
_maxWritesPerFrame: number; | ||
components: { | ||
[typeName: string]: ComponentStats; | ||
}; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
_maxLimboEntities: number; | ||
get maxLimboEntities(): number; | ||
set maxLimboEntities(value: number); | ||
_numRefs: number; | ||
maxRefs: number; | ||
get maxLimboComponents(): number; | ||
set maxLimboComponents(value: number); | ||
get numRefs(): number; | ||
set numRefs(value: number); | ||
_maxShapeChangesPerFrame: number; | ||
get maxShapeChangesPerFrame(): number; | ||
set maxShapeChangesPerFrame(value: number); | ||
_maxWritesPerFrame: number; | ||
get maxWritesPerFrame(): number; | ||
set maxWritesPerFrame(value: number); | ||
for(type: ComponentType<any>): ComponentStats; | ||
toString(): string; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
defs: DefsArray; | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxLimboComponents?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defaultComponentStorage?: ComponentStorage; | ||
} | ||
declare class Dispatcher { | ||
readonly maxEntities: number; | ||
readonly indexer: Indexer; | ||
readonly defaultComponentStorage: ComponentStorage; | ||
readonly registry: Registry; | ||
@@ -272,3 +251,3 @@ readonly systems: SystemBox[]; | ||
private readonly callbackSystem; | ||
constructor({ defs, maxEntities, maxLimboEntities, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame }: WorldOptions); | ||
constructor({ defs, maxEntities, maxLimboEntities, maxLimboComponents, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame, defaultComponentStorage }: WorldOptions); | ||
private normalizeAndInitSystems; | ||
@@ -299,2 +278,3 @@ private splitDefs; | ||
private readonly shapes; | ||
private readonly staleShapes; | ||
private readonly entityIdPool; | ||
@@ -306,7 +286,13 @@ readonly pool: EntityPool; | ||
private readonly oldDeletionPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
private readonly removalLog; | ||
private readonly prevRemovalPointer; | ||
private readonly oldRemovalPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, maxLimboComponents: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
createEntity(initialComponents: (ComponentType<any> | any)[]): Entity; | ||
queueDeletion(id: EntityId): void; | ||
queueRemoval(id: EntityId, type: ComponentType<any>): void; | ||
flush(): void; | ||
processEndOfFrame(): void; | ||
private processDeletionLog; | ||
private processRemovalLog; | ||
extendMaskAndSetFlag(mask: number[], type: ComponentType<any>): void; | ||
@@ -350,3 +336,4 @@ maskHasFlag(mask: number[] | undefined, type: ComponentType<any>): boolean; | ||
constructor(defaultValue: JSType); | ||
abstract define<C>(binding: Binding<C>, name: string, buffer?: SharedArrayBuffer): SharedArrayBuffer; | ||
abstract defineElastic(binding: Binding<any>, field: Field<any>): void; | ||
abstract defineFixed(binding: Binding<any>, field: Field<any>): void; | ||
static boolean: Type<boolean>; | ||
@@ -364,2 +351,4 @@ static uint8: Type<number>; | ||
static ref: Type<Entity | null>; | ||
static object: Type<any>; | ||
static weakObject: Type<any>; | ||
} | ||
@@ -373,2 +362,8 @@ interface SchemaDef<JSType> { | ||
} | ||
type ComponentStorage = "sparse" | "packed" | "compact"; | ||
interface ComponentOptions { | ||
storage?: ComponentStorage; | ||
capacity?: number; | ||
initialCapacity?: number; | ||
} | ||
interface Field<JSType> { | ||
@@ -378,2 +373,5 @@ name: string; | ||
default: JSType; | ||
buffer?: SharedArrayBuffer; | ||
localBuffer?: any[]; | ||
updateBuffer?(): void; | ||
} | ||
@@ -383,19 +381,27 @@ interface ComponentType<C> { | ||
schema?: Schema; | ||
maxEntities?: number; | ||
__id?: number; | ||
__flagOffset?: number; | ||
__flagMask?: number; | ||
__trackedWrites?: boolean; | ||
__fields?: Field<any>[]; | ||
options?: ComponentOptions; | ||
/** | ||
* A unique, sequential id number for this component type, assigned automatically by becsy. It | ||
* will stay the same across runs as long as the list of defs used to create the world doesn't | ||
* change. Feel free to use this for your own purposes but don't change it. | ||
*/ | ||
id?: number; | ||
__binding?: Binding<C>; | ||
__bind?(id: EntityId, writable: boolean): C; | ||
__create?(id: EntityId): C; | ||
__delete?(id: EntityId): void; | ||
} | ||
declare class Binding<C> { | ||
readonly type: ComponentType<C>; | ||
readonly fields: Field<any>[]; | ||
readonly dispatcher: Dispatcher; | ||
readonly maxEntities: number; | ||
capacity: number; | ||
readonly readonlyInstance: C; | ||
readonly writableInstance: C; | ||
readonly flagOffset: number; | ||
readonly flagMask: number; | ||
trackedWrites: boolean; | ||
entityId: number; | ||
index: number; | ||
constructor(type: ComponentType<C>, dispatcher: Dispatcher, maxEntities: number); | ||
constructor(type: ComponentType<C>, fields: Field<any>[], dispatcher: Dispatcher, capacity: number); | ||
} | ||
@@ -408,3 +414,3 @@ declare class World { | ||
execute(time?: number, delta?: number): void; | ||
get stats(): any; | ||
get stats(): Stats; | ||
} | ||
@@ -418,3 +424,4 @@ interface PropOptions<JSType> { | ||
declare function component(constructor: ComponentType<any>): void; | ||
declare function component(options: ComponentOptions): (constructor: ComponentType<any>) => void; | ||
export { World, Type, Entity, System, SystemType, componentTypes, component, prop, Query, ComponentType }; | ||
//# sourceMappingURL=index.umd.min.d.ts.map |
@@ -1,2 +0,2 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).becsy={})}(this,(function(e){"use strict";const t=new TextEncoder,s=new TextDecoder;function r(e){throw new Error(`Component is not writable; use entity.write(${e.type.name}) to acquire a writable version`)}class i{defaultValue;constructor(e){this.defaultValue=e}static boolean;static uint8;static int8;static uint16;static int16;static uint32;static int32;static float32;static float64;static staticString;static dynamicString;static ref}class n extends i{NumberArray;constructor(e){super(0),this.NumberArray=e}define(e,t,s){s||(s=new SharedArrayBuffer(e.maxEntities*this.NumberArray.BYTES_PER_ELEMENT));const i=new this.NumberArray(s);return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){i[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){r(e)}}),s}}class a extends i{choices;choicesIndex=new Map;TypedArray;constructor(e){if(super(e[0]),this.choices=e,!e?.length)throw new Error("No choices specified for Type.staticString");e.length<256?this.TypedArray=Uint8Array:e.length<65536?this.TypedArray=Uint16Array:this.TypedArray=Uint32Array;for(let t=0;t<e.length;t++)this.choicesIndex.set(e[t],t)}define(e,t,s){s||(s=new SharedArrayBuffer(e.maxEntities*this.TypedArray.BYTES_PER_ELEMENT));const i=new this.TypedArray(s),n=this.choices,a=this.choicesIndex;return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){const s=a.get(t);if(void 0===s)throw new Error(`Static string not in set: "${t}"`);i[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){r(e)}}),s}}class o extends i{maxUtf8Length;lengthsStride;bytesStride;constructor(e){super(""),this.maxUtf8Length=e+e%2,this.lengthsStride=e/2+1,this.bytesStride=this.maxUtf8Length+2}define(e,i,n){if(!n){const t=e.maxEntities*(this.maxUtf8Length+Uint16Array.BYTES_PER_ELEMENT);n=new SharedArrayBuffer(t)}const a=new Uint16Array(n),o=new Uint8Array(n),h=this.maxUtf8Length,d=this.lengthsStride,c=this.bytesStride;return Object.defineProperty(e.writableInstance,i,{enumerable:!0,configurable:!0,get(){const t=a[e.index*d];return s.decode(new Uint8Array(o.buffer,e.index*c+2,t))},set(s){const r=t.encode(s);if(r.byteLength>h)throw new Error(`Dynamic string length > ${h} after encoding: ${s}`);a[e.index*d]=r.byteLength,o.set(r,e.index*c+2)}}),Object.defineProperty(e.readonlyInstance,i,{enumerable:!0,configurable:!0,get(){const t=a[e.index*d];return s.decode(new Uint8Array(o.buffer,e.index*c+2,t))},set(t){r(e)}}),n}}i.boolean=new class extends i{constructor(){super(!1)}define(e,t,s){s||(s=new SharedArrayBuffer(e.maxEntities));const i=new Uint8Array(s);return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){i[e.index]=t?1:0}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){r(e)}}),s}},i.uint8=new n(Uint8Array),i.int8=new n(Int8Array),i.uint16=new n(Uint16Array),i.int16=new n(Int16Array),i.uint32=new n(Uint32Array),i.int32=new n(Int32Array),i.float32=new n(Float32Array),i.float64=new n(Float64Array),i.staticString=e=>new a(e),i.dynamicString=e=>new o(e),i.ref=new class extends i{constructor(){super(null)}define(e,t,s){s||(s=new SharedArrayBuffer(e.maxEntities*Int32Array.BYTES_PER_ELEMENT));const i=new Int32Array(s);return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){const s=i[e.index],r=t?.__id??-1;if(s===r)return;const n=e.dispatcher.indexer;0!==s&&n.remove(s,e.entityId),i[e.index]=r,0!==r&&n.insert(r,e.entityId)}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){r(e)}}),s}};class h{type;dispatcher;maxEntities;readonlyInstance;writableInstance;entityId=0;index=0;constructor(e,t,s){this.type=e,this.dispatcher=t,this.maxEntities=s,this.readonlyInstance=new e,this.writableInstance=new e}}function d(e,t,s){if((t.maxEntities??0)>s.maxEntities)throw new Error(`Component type ${t.name} maxEntities higher than world maxEntities; reduce ${t.maxEntities} to or below ${s.maxEntities}`);if(("undefined"==typeof process||"test"!==process.env.NODE_ENV)&&t.__bind)throw new Error(`Component type ${t.name} is already in use in another world`);const r=Math.min(t.maxEntities??s.maxEntities,s.maxEntities);s.maxEntities,t.__id=e,t.__flagOffset=e>>5,t.__flagMask=1<<(31&e);const n=new h(t,s,r);t.__bind=(e,t)=>(n.entityId=e,n.index=e,t?n.writableInstance:n.readonlyInstance),t.__fields=function(e){const t=e.schema,s=[];for(const e in t){const r=t[e];let n;n=r instanceof i?{name:e,default:r.defaultValue,type:r}:Object.assign({name:e,default:r.type.defaultValue},r),s.push(n)}return s}(t);for(const e of t.__fields)e.type.define(n,e.name)}const c=4194303,l=Object.freeze({});class u{__registry;__id;joined;constructor(e){this.__registry=e}__reset(e){this.__id=e,this.joined=l}add(e,t){if(this.__checkMask(e,!0),this.__registry.hasFlag(this.__id,e))throw new Error(`Entity already has a ${e.name} component`);return this.__registry.setFlag(this.__id,e),function(e,t,s){if(void 0!==s)for(const t in s)if(!e.schema?.[t])throw new Error(`Property ${t} not defined for component ${e.name}`);const r=e.__bind(t,!0);for(const t of e.__fields)r[t.name]=s?.[t.name]??t.default}(e,this.__id,t),this}addAll(...e){for(let t=0;t<e.length;t++){const s=e[t];if("function"!=typeof s)throw new Error(`Bad arguments to bulk add: expected component type, got: ${s}`);let r=e[t+1];"function"==typeof r?r=void 0:t++,this.add(s,r)}return this}remove(e){if(!this.has(e))throw new Error(`Entity doesn't have a ${e.name} component`);this.__remove(e)}removeAll(...e){for(const t of e)this.remove(t)}has(e,t=!1){return this.__checkMask(e,!1),this.__registry.hasFlag(this.__id,e,t)}read(e){if(!this.has(e))throw new Error(`Entity doesn't have a ${e.name} component`);return e.__bind(this.__id,!1)}readRecentlyRemoved(e){if(!this.has(e,!0))throw new Error(`Entity doesn't have a ${e.name} component`);return e.__bind(this.__id,!1)}write(e){if(!this.has(e,!0))throw new Error(`Entity doesn't have a ${e.name} component`);return e.__trackedWrites&&this.__registry.trackWrite(this.__id,e),e.__bind(this.__id,!0)}delete(){for(const e of this.__registry.types)this.__registry.hasFlag(this.__id,e)&&(this.__checkMask(e,!0),this.__remove(e));this.__registry.queueDeletion(this.__id),this.__wipeInboundRefs()}__remove(e){this.__deindexOutboundRefs(e),this.__registry.clearFlag(this.__id,e)}__deindexOutboundRefs(e){const t=e.__fields;if(t.some((e=>e.type===i.ref))){const s=this.write(e);for(const e of t)e.type===i.ref&&(s[e.name]=null)}}__wipeInboundRefs(){}__checkMask(e,t){const s=this.__registry.executingSystem?.rwMasks,r=t?s?.write:s?.read;if(r&&!this.__registry.maskHasFlag(r,e))throw new Error(`System didn't mark component ${e.name} as ${t?"writable":"readable"}`)}}class m{maxRefs;buffer;index;numRefs=0;constructor(e){this.maxRefs=e,this.index=new Uint32Array(this.buffer=new SharedArrayBuffer(8*e))}insert(e,t){if(this.numRefs>=this.maxRefs)throw new Error(`Max number of refs reached: ${this.maxRefs}`);const s=this.findIndex(e,t);if(this.index[2*s]===e&&this.index[2*s+1]===t)throw new Error(`Internal error; ref already indexed: ${t} -> ${e}`);this.index.copyWithin(2*(s+1),2*s,2*(this.numRefs+1)),this.index[2*s]=e,this.index[2*s+1]=t,this.numRefs+=1}remove(e,t){const s=this.findIndex(e,t);if(this.index[2*s]===e&&this.index[2*s+1]===t)throw new Error(`Internal error; ref not found: ${t} -> ${e}`);this.numRefs-=1,this.index.copyWithin(s,s+1,this.numRefs-s)}*iterateReferrers(e){}findIndex(e,t){let s=0,r=this.numRefs-1,i=1;for(;s<r;){i=Math.floor((r-s+1)/2)+s;const n=this.index[2*i];n===e?void 0!==t&&this.index[2*i+1]<t?s=i+1:r=i-1:n<e?s=i+1:r=i-1}return s>=r&&(i=Math.max(0,Math.min(this.numRefs+1,s))),i}}class f{size;bytes;constructor(e){this.size=e,this.bytes=new Uint32Array(Math.ceil(e/32))}get(e){if(e<0||e>=this.size)throw new Error(`Bit index out of bounds: ${e}`);return 0!=(this.bytes[e>>>5]&1<<(31&e))}set(e){if(e<0||e>=this.size)throw new Error(`Bit index out of bounds: ${e}`);this.bytes[e>>>5]|=1<<(31&e)}unset(e){if(e<0||e>=this.size)throw new Error(`Bit index out of bounds: ${e}`);this.bytes[e>>>5]&=~(1<<(31&e))}clear(){this.bytes.fill(0)}}const _=[];class g{maxEntries;configParamName;data;corral;constructor(e,t){this.maxEntries=e,this.configParamName=t;const s=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.data=new Uint32Array(s);const r=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.corral=new Uint32Array(r)}push(e){const t=this.corral[0];t>=this.maxEntries&&this.throwCapacityExceeded(),t&&this.corral[t]===e||(this.corral[t+2]=e,this.corral[0]+=1)}commit(){const e=this.corral[0];if(!e)return;let t=this.data[0];const s=Math.min(e,this.maxEntries-t);for(this.data.set(this.corral.subarray(2,s+2),t+2),s<e&&this.data.set(this.corral.subarray(s+2,e+2),2),t+=e;t>=this.maxEntries;)t-=this.maxEntries,this.data[1]+=1;this.data[0]=t,this.corral[0]=0,this.corral[1]+=1}createPointer(e){return e?(e.index=this.data[0],e.generation=this.data[1],e.corralIndex=this.corral[0],e.corralGeneration=this.corral[1],e):{index:this.data[0],generation:this.data[1],corralIndex:this.corral[0],corralGeneration:this.corral[1]}}hasUpdatesSince(e){return this.checkPointer(e),!(e.index===this.data[0]&&e.generation===this.data[1]&&(e.corralGeneration===this.corral[1]?e.corralIndex===this.corral[0]:0===this.corral[0]))}processSince(e,t){this.checkPointers(e,t);let s=_;const r=t?.index??this.data[0],i=t?.generation??this.data[1];if(e.generation===i)if(e.index<r)s=[this.data,e.index+2,r+2],e.index=r;else{const t=this.corral[0],r=this.corral[1];(e.corralGeneration===r?e.corralIndex<t:t)&&(s=[this.corral,e.corralIndex+2,t+2],e.corralIndex=t,e.corralGeneration=r)}else s=[this.data,e.index+2,this.data.length],e.index=0,e.generation=i;return s}countSince(e,t){if(this.checkPointers(e,t),this.corral[0])throw new Error("Internal error, should commit log before counting");const s=e.index,r=t?.index??this.data[0],i=t?.generation??this.data[1];return e.index=r,e.generation=i,s===r&&e.generation===i?0:s<r?r-s:this.maxEntries-(s-r)}checkPointers(e,t){if(this.checkPointer(e),t&&(this.checkPointer(t),e.index>t.index&&e.generation>=t.generation))throw new RangeError("Internal error, start pointer exceeds end pointer")}checkPointer(e){const t=this.data[0];let s=e.generation;if(e.index===t?s+1<this.data[1]&&this.throwCapacityExceeded():(e.index>t&&(s+=1),s!==this.data[1]&&this.throwCapacityExceeded()),e.corralGeneration>this.corral[1])throw new Error("Internal error, pointer corral generation older than corral");if(e.corralGeneration===this.corral[1]&&e.corralIndex>this.corral[0])throw new Error("Internal error, pointer past end of log corral area")}throwCapacityExceeded(){throw new Error(`Log capacity exceeded, please raise ${this.configParamName} above ${this.maxEntries}`)}}class y{maxItems;configParamName;data;constructor(e,t){this.maxItems=e,this.configParamName=t,this.data=new Uint32Array(new SharedArrayBuffer((e+1)*Uint32Array.BYTES_PER_ELEMENT))}get length(){return this.data[0]}take(){const e=this.data[0]--;if(e<=0)throw new RangeError(`Pool capacity exceeded, please raise ${this.configParamName} above ${this.maxItems}`);return this.data[e]}refill(e){if(!e.length)return;const t=this.length,s=t+e.length;if(s>this.maxItems)throw new Error("Internal error, refill exceeded pool capacity");this.data.set(e,t+1),this.data[0]=s}fillWithDescendingIntegers(e){const t=this.length;for(let s=this.data.length-1;s>t;s--)this.data[s]=e++;this.data[0]=this.data.length-1}}class p{pool;entities=[];constructor(e){this.pool=e}add(e){this.entities.push(this.pool.borrowTemporarily(e))}clear(){this.entities.length&&this.entities.splice(0,1/0)}}class w{pool;entities=[];lookupTable;constructor(e,t){this.pool=e,this.lookupTable=new Int32Array(t),this.lookupTable.fill(-1)}add(e){const t=this.entities.push(this.pool.borrow(e))-1;this.lookupTable[e]=t}remove(e){const t=this.lookupTable[e];if(t<0)throw new Error("Internal error, entity not in list");this.pool.return(e),this.lookupTable[e]=-1;const s=this.entities.pop();t<this.entities.length&&(this.entities[t]=s,this.lookupTable[s.__id]=t)}has(e){return this.lookupTable[e]>=0}clear(){throw new Error("Internal error, trying to clear persistent entity list")}}var x;!function(e){e[e.all=1]="all",e[e.added=2]="added",e[e.removed=4]="removed",e[e.changed=8]="changed",e[e.addedOrChanged=16]="addedOrChanged",e[e.changedOrRemoved=32]="changedOrRemoved",e[e.addedChangedOrRemoved=64]="addedChangedOrRemoved"}(x||(x={}));const E=x.added|x.removed|x.changed|x.addedOrChanged|x.changedOrRemoved|x.addedChangedOrRemoved,b=x.changed|x.addedOrChanged|x.changedOrRemoved|x.addedChangedOrRemoved;class v{query;system;results={};flavors=0;__withMask;__withoutMask;__trackMask;__refMask;hasTransientResults;hasChangedResults;currentEntities;changedEntities;constructor(e,t){this.query=e,this.system=t,e.__results=this.results,e.__systemName=t.name}complete(){const e=this.system.dispatcher;if(this.hasTransientResults=Boolean(this.flavors&E),this.hasChangedResults=Boolean(this.flavors&b),this.hasChangedResults&&!this.__trackMask)throw new Error("Query for changed entities must track at least one component");this.flavors&x.all?this.results.all=new w(e.registry.pool,e.maxEntities):this.currentEntities=new f(e.maxEntities),this.hasTransientResults&&this.allocateTransientResultLists(),this.flavors&&this.system.shapeQueries.push(this),this.hasChangedResults&&(this.changedEntities=new f(e.maxEntities),this.system.writeQueries.push(this))}allocateTransientResultLists(){this.flavors&x.added&&this.allocateResult("added"),this.flavors&x.removed&&this.allocateResult("removed"),this.flavors&x.changed&&this.allocateResult("changed"),this.flavors&x.addedOrChanged&&this.allocateResult("addedOrChanged"),this.flavors&x.changedOrRemoved&&this.allocateResult("changedOrRemoved"),this.flavors&x.addedChangedOrRemoved&&this.allocateResult("addedChangedOrRemoved")}allocateResult(e){const t=this.system.dispatcher;this.results[e]=new p(t.registry.pool)}clearTransientResults(){this.hasTransientResults&&(this.results.added?.clear(),this.results.removed?.clear(),this.results.changed?.clear(),this.results.addedOrChanged?.clear(),this.results.changedOrRemoved?.clear(),this.results.addedChangedOrRemoved?.clear(),this.changedEntities?.clear())}handleShapeUpdate(e){const t=this.system.dispatcher.registry,s=this.results.all?.has(e)??this.currentEntities.get(e),r=t.matchShape(e,this.__withMask,this.__withoutMask);r&&!s?(this.currentEntities?.set(e),this.results.all?.add(e),this.results.added?.add(e),this.results.addedOrChanged?.add(e),this.results.addedChangedOrRemoved?.add(e)):!r&&s&&(this.currentEntities?.unset(e),this.system.removedEntities.set(e),this.results.all?.remove(e),this.results.removed?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}handleWrite(e,t,s){!this.changedEntities.get(e)&&(this.__trackMask[t]??0)&s&&(this.changedEntities.set(e),this.results.changed?.add(e),this.results.addedOrChanged?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}}class R{__callback;__userQuery;__query;__system;__lastTypes;constructor(e,t){this.__callback=e,this.__userQuery=t}__build(e){try{this.__system=e,this.__query=new v(this.__userQuery,e),this.__callback(this),this.__query.complete()}catch(t){throw t.message=`Failed to build query in system ${e.name}: ${t.message}`,t}}get and(){return this}get but(){return this}get also(){return this}get all(){return this.__query.flavors|=x.all,this}get added(){return this.__query.flavors|=x.added,this}get removed(){return this.__query.flavors|=x.removed,this}get changed(){return this.__query.flavors|=x.changed,this}get addedOrChanged(){return this.__query.flavors|=x.addedOrChanged,this}get changedOrRemoved(){return this.__query.flavors|=x.changedOrRemoved,this}get addedChangedOrRemoved(){return this.__query.flavors|=x.addedChangedOrRemoved,this}with(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withMask"),this}without(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withoutMask",e),this}using(...e){return this.set(this.__system.rwMasks.read,e),this}get track(){this.set("__trackMask");for(const e of this.__lastTypes)e.__trackedWrites=!0;return this}get read(){return this}get write(){return this.set(this.__system.rwMasks.write),this}set(e,t,s){if(e){if(t||(t=this.__lastTypes),!t)throw new Error("No component type to apply query modifier to");if(this.__lastTypes=t,"string"==typeof e){if(s&&this.__query[e])throw new Error(`Only one ${s} allowed`);this.__query[e]||(this.__query[e]=[]),e=this.__query[e]}else if(s&&e.some((e=>0!==e)))throw new Error(`Only one ${s} allowed`);for(const s of t)this.__system.dispatcher.registry.extendMaskAndSetFlag(e,s)}}}class k{__results;__systemName;get all(){return this.__checkList("all"),this.__results.all.entities}get added(){return this.__checkList("added"),this.__results.added.entities}get removed(){return this.__checkList("removed"),this.__results.removed.entities}get changed(){return this.__checkList("changed"),this.__results.changed.entities}get addedOrChanged(){return this.__checkList("addedOrChanged"),this.__results.addedOrChanged.entities}get changedOrRemoved(){return this.__checkList("changedOrRemoved"),this.__results.changedOrRemoved.entities}get addedChangedOrRemoved(){return this.__checkList("addedChangedOrRemoved"),this.__results.addedChangedOrRemoved.entities}__checkList(e){if(!this.__results[e])throw new Error(`Query '${e}' not configured, please add .${e} to your query definition in system ${this.__systemName}`)}}class P{__queryBuilders=[];__dispatcher;time;delta;get name(){return this.constructor.name}query(e){const t=new k,s=new R(e,t);if(!this.__queryBuilders)throw new Error(`Attempt to create a new query after world initialized in system ${this.name}`);return this.__queryBuilders.push(s),t}createEntity(...e){return this.__dispatcher.createEntity(e)}}class S{system;dispatcher;rwMasks={read:[],write:[]};shapeQueries=[];writeQueries=[];hasWriteQueries;processedEntities;removedEntities;shapeLogPointer;writeLogPointer;get name(){return this.system.name}constructor(e,t){this.system=e,this.dispatcher=t,e.__dispatcher=t,this.shapeLogPointer=t.shapeLog.createPointer(),this.writeLogPointer=t.writeLog?.createPointer(),this.processedEntities=new f(t.maxEntities),this.removedEntities=new f(t.maxEntities);for(const t of e.__queryBuilders)t.__build(this);e.__queryBuilders=null,this.hasWriteQueries=!!this.writeQueries.length}execute(e,t){this.system.time=e,this.system.delta=t,this.runQueries(),this.system.execute()}runQueries(){const e=this.dispatcher.shapeLog.hasUpdatesSince(this.shapeLogPointer),t=this.hasWriteQueries&&this.dispatcher.writeLog.hasUpdatesSince(this.writeLogPointer);if(e||t){this.processedEntities.clear();for(const e of this.shapeQueries)e.clearTransientResults();e&&this.__updateShapeQueries(),t&&this.__updateWriteQueries()}}__updateShapeQueries(){const e=this.dispatcher.shapeLog;let t,s,r;for(;[t,s,r]=e.processSince(this.shapeLogPointer),t;)for(let e=s;e<r;e++){const s=t[e];if(!this.processedEntities.get(s)){this.processedEntities.set(s);for(const e of this.shapeQueries)e.handleShapeUpdate(s)}}}__updateWriteQueries(){const e=this.dispatcher.writeLog;let t,s,r;for(;[t,s,r]=e.processSince(this.writeLogPointer),t;)for(let e=s;e<r;e++){const s=t[e],r=4194303&s;if(!this.processedEntities.get(r)){const e=s>>>22;for(const t of this.writeQueries)t.handleWrite(r,e>>5,1<<(31&e))}}}}class L{registry;borrowed;borrowCounts;spares=[];temporarilyBorrowedIds=[];constructor(e,t){this.registry=e,this.borrowed=Array.from({length:t}),this.borrowCounts=new Int32Array(t)}borrow(e){this.borrowCounts[e]+=1;let t=this.borrowed[e];return t||(t=this.borrowed[e]=this.spares.pop()??new u(this.registry),t.__reset(e)),t}borrowTemporarily(e){const t=this.borrow(e);return this.temporarilyBorrowedIds.push(e),t}returnTemporaryBorrows(){for(const e of this.temporarilyBorrowedIds)this.return(e);this.temporarilyBorrowedIds.splice(0,1/0)}return(e){if(!this.borrowCounts[e])throw new Error("Internal error, returning entity with no borrows");--this.borrowCounts[e]<=0&&(this.spares.push(this.borrowed[e]),this.borrowed[e]=void 0)}}class O{types;dispatcher;stride;shapes;entityIdPool;pool;executingSystem;deletionLog;prevDeletionPointer;oldDeletionPointer;constructor(e,t,s,r){this.types=s,this.dispatcher=r;let i=0;for(const e of s)d(i++,e,this.dispatcher);this.stride=Math.ceil(s.length/32);const n=e*this.stride*4;this.shapes=new Uint32Array(new SharedArrayBuffer(n)),this.entityIdPool=new y(e,"maxEntities"),this.entityIdPool.fillWithDescendingIntegers(0),this.pool=new L(this,e),this.deletionLog=new g(t,"maxLimboEntities"),this.prevDeletionPointer=this.deletionLog.createPointer(),this.oldDeletionPointer=this.deletionLog.createPointer()}createEntity(e){const t=this.entityIdPool.take();this.shapes.fill(0,t*this.stride,(t+1)*this.stride);const s=this.pool.borrowTemporarily(t);return e&&s.addAll(...e),this.dispatcher.stats.numEntities+=1,s}queueDeletion(e){this.deletionLog.push(e)}flush(){this.pool.returnTemporaryBorrows(),this.deletionLog.commit()}processEndOfFrame(){this.deletionLog.commit();let e,t,s,r=0;for(;[e,t,s]=this.deletionLog.processSince(this.oldDeletionPointer,this.prevDeletionPointer),e;){const i=e.subarray(t,s);this.entityIdPool.refill(i),r+=i.length}this.dispatcher.stats.numEntities-=r,this.dispatcher.stats.maxLimboEntities=r,this.deletionLog.createPointer(this.prevDeletionPointer)}extendMaskAndSetFlag(e,t){const s=t.__flagOffset;s>=e.length&&(e.length=s+1,e.fill(0,e.length,s)),e[s]|=t.__flagMask}maskHasFlag(e,t){return 0!=((e?.[t.__flagOffset]??0)&t.__flagMask)}hasFlag(e,t,s=!1){const r=e*this.stride+t.__flagOffset;return 0!=(this.shapes[r]&t.__flagMask)||!(!s||!this.executingSystem?.removedEntities.get(e)||0==((this.executingSystem.rwMasks.read?.[t.__flagOffset]??0)&t.__flagMask))}setFlag(e,t){this.shapes[e*this.stride+t.__flagOffset]|=t.__flagMask,this.dispatcher.shapeLog.push(e)}clearFlag(e,t){this.shapes[e*this.stride+t.__flagOffset]&=~t.__flagMask,this.dispatcher.shapeLog.push(e)}trackWrite(e,t){this.dispatcher.writeLog.push(e|t.__id<<22)}matchShape(e,t,s){const r=e*this.stride;if(t)for(let e=0;e<t.length;e++){const s=t[e];if((this.shapes[r+e]&s)!==s)return!1}if(s)for(let e=0;e<s.length;e++){const t=s[e];if(0!=(this.shapes[r+e]&t))return!1}return!0}}const I="undefined"!=typeof window&&void 0!==window.performance?performance.now.bind(performance):Date.now.bind(Date);class T{frames=0;_numEntities=0;maxEntities=0;get numEntities(){return this._numEntities}set numEntities(e){this._numEntities=e,e>this.maxEntities&&(this.maxEntities=e)}_maxLimboEntities=0;get maxLimboEntities(){return this._maxLimboEntities}set maxLimboEntities(e){e>this._maxLimboEntities&&(this._maxLimboEntities=e)}_numRefs=0;maxRefs=0;get numRefs(){return this._numRefs}set numRefs(e){this._numRefs=e,e>this.maxRefs&&(this.maxRefs=e)}_maxShapeChangesPerFrame=0;get maxShapeChangesPerFrame(){return this._maxShapeChangesPerFrame}set maxShapeChangesPerFrame(e){e>this._maxShapeChangesPerFrame&&(this._maxShapeChangesPerFrame=e)}_maxWritesPerFrame=0;get maxWritesPerFrame(){return this._maxWritesPerFrame}set maxWritesPerFrame(e){e>this._maxWritesPerFrame&&(this._maxWritesPerFrame=e)}toString(){return`World stats:\n frames: ${this.frames}\n entities: ${this.numEntities} of ${this.maxEntities} max (${this.maxLimboEntities} limbo max)\n refs: ${this.numRefs} of ${this.maxRefs} max\n logs: ${this.maxShapeChangesPerFrame} shape changes/frame max, ${this.maxWritesPerFrame} writes/frame max`}}class A extends P{__callback;execute(){this.__callback(this)}}class C{maxEntities;indexer;registry;systems;lastTime=I()/1e3;executing;shapeLog;writeLog;shapeLogFramePointer;writeLogFramePointer;stats;userCallbackSystem;callbackSystem;constructor({defs:e,maxEntities:t=1e4,maxLimboEntities:s=Math.ceil(t/5),maxRefs:r=t,maxShapeChangesPerFrame:i=2*t,maxWritesPerFrame:n=4*t}){if(t>c)throw new Error("maxEntities too high, the limit is 4194303");const{componentTypes:a,systemTypes:o}=this.splitDefs(e);if(a.length>1024)throw new Error("Too many component types, the limit is 1024");this.stats=new T,this.maxEntities=t,this.shapeLog=new g(i,"maxShapeChangesPerFrame"),this.shapeLogFramePointer=this.shapeLog.createPointer(),this.indexer=new m(r),this.registry=new O(t,s,a.flat(1/0),this),this.systems=this.normalizeAndInitSystems(o),this.systems.some((e=>e.hasWriteQueries))&&(this.writeLog=new g(n,"maxWritesPerFrame"),this.writeLogFramePointer=this.writeLog.createPointer()),this.userCallbackSystem=new A,this.callbackSystem=new S(this.userCallbackSystem,this)}normalizeAndInitSystems(e){const t=[],s=e.flat(1/0);for(let e=0;e<s.length;e++){const r=new s[e],i=s[e+1];i&&"function"!=typeof i&&(Object.assign(r,i),e++),t.push(new S(r,this))}return t}splitDefs(e){const t=[],s=[];let r=!1;for(const i of e.flat(1/0))if("function"==typeof i)r=!i.schema,(r?s:t).push(i);else{if(!r)throw new Error("Unexpected value in world defs: "+i);s.push(i),r=!1}return{componentTypes:t,systemTypes:s}}execute(e,t,s){if(this.executing)throw new Error("Recursive system execution not allowed");this.executing=!0,void 0===e&&(e=I()/1e3),void 0===t&&(t=e-this.lastTime),this.lastTime=e;for(const r of s??this.systems)this.registry.executingSystem=r,r.execute(e,t),this.flush();this.registry.executingSystem=void 0,this.registry.processEndOfFrame(),this.executing=!1,this.gatherFrameStats()}executeFunction(e){if(this.executing)throw new Error("Ad hoc function execution not allowed while world is executing");this.executing=!0,this.userCallbackSystem.__callback=e,this.callbackSystem.execute(0,0),this.flush(),this.registry.processEndOfFrame(),this.executing=!1,this.gatherFrameStats()}gatherFrameStats(){this.stats.frames+=1,this.stats.maxShapeChangesPerFrame=this.shapeLog.countSince(this.shapeLogFramePointer),this.stats.maxWritesPerFrame=this.writeLog?.countSince(this.writeLogFramePointer)??0}flush(){this.registry.flush(),this.shapeLog.commit(),this.writeLog?.commit()}createEntity(e){const t=this.registry.createEntity(e);return this.executing||this.flush(),t}}const F=[];e.Entity=u,e.Query=k,e.System=P,e.Type=i,e.World=class{__dispatcher;constructor(e){this.__dispatcher=new C(e)}build(e){this.__dispatcher.executeFunction(e)}createEntity(...e){this.__dispatcher.createEntity(e)}execute(e,t){this.__dispatcher.execute(e,t)}get stats(){return this.__dispatcher.stats}},e.component=function(e){F.push(e)},e.componentTypes=F,e.prop=function(e){return function(t,s){t.constructor.schema||(t.constructor.schema={});const r="type"in e?e:{type:e};t.constructor.schema[s]=r}},Object.defineProperty(e,"__esModule",{value:!0})})); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).becsy={})}(this,(function(e){"use strict";const t=new TextEncoder,s=new TextDecoder;function i(e){throw new Error(`Component is not writable; use entity.write(${e.type.name}) to acquire a writable version`)}class r{defaultValue;constructor(e){this.defaultValue=e}static boolean;static uint8;static int8;static uint16;static int16;static uint32;static int32;static float32;static float64;static staticString;static dynamicString;static ref;static object;static weakObject}class n extends r{NumberArray;constructor(e){super(0),this.NumberArray=e}defineElastic(e,t){let s,r;t.updateBuffer=()=>{const i=e.capacity*this.NumberArray.BYTES_PER_ELEMENT,n=t.buffer?.byteLength!==i;(n||t.buffer!==s)&&(s=n?new SharedArrayBuffer(i):t.buffer,r=new this.NumberArray(s),n&&t.buffer&&r.set(new this.NumberArray(t.buffer)),t.buffer=s)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>r[e.index],set(t){r[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>r[e.index],set(t){i(e)}})}defineFixed(e,t){const s=e.capacity*this.NumberArray.BYTES_PER_ELEMENT,r=new SharedArrayBuffer(s),n=new this.NumberArray(r);t.buffer=r,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>n[e.index],set(t){n[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>n[e.index],set(t){i(e)}})}}class a extends r{choices;choicesIndex=new Map;TypedArray;constructor(e){if(super(e[0]),this.choices=e,!e?.length)throw new Error("No choices specified for Type.staticString");e.length<256?this.TypedArray=Uint8Array:e.length<65536?this.TypedArray=Uint16Array:this.TypedArray=Uint32Array;for(let t=0;t<e.length;t++)this.choicesIndex.set(e[t],t)}defineElastic(e,t){let s,r;const n=this.choices,a=this.choicesIndex;t.updateBuffer=()=>{const i=e.capacity*this.TypedArray.BYTES_PER_ELEMENT,n=t.buffer?.byteLength!==i;(n||t.buffer!==s)&&(s=n?new SharedArrayBuffer(i):t.buffer,r=new this.TypedArray(s),n&&t.buffer&&r.set(new this.TypedArray(t.buffer)),t.buffer=s)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=r[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){const s=a.get(t);if(void 0===s)throw new Error(`Static string not in set: "${t}"`);r[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=r[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){i(e)}})}defineFixed(e,t){const s=this.choices,r=this.choicesIndex,n=e.capacity*this.TypedArray.BYTES_PER_ELEMENT,a=new SharedArrayBuffer(n),o=new this.TypedArray(a);t.buffer=a,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=o[e.index],i=s[t];if(void 0===i)throw new Error(`Invalid static string index: ${t}`);return i},set(t){const s=r.get(t);if(void 0===s)throw new Error(`Static string not in set: "${t}"`);o[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=o[e.index],i=s[t];if(void 0===i)throw new Error(`Invalid static string index: ${t}`);return i},set(t){i(e)}})}}class o extends r{maxUtf8Length;lengthsStride;bytesStride;constructor(e){super(""),this.maxUtf8Length=e+e%2,this.lengthsStride=e/2+1,this.bytesStride=this.maxUtf8Length+2}defineElastic(e,r){let n,a,o;const h=this.maxUtf8Length,d=this.lengthsStride,c=this.bytesStride;r.updateBuffer=()=>{const t=e.capacity*(this.maxUtf8Length+Uint16Array.BYTES_PER_ELEMENT),s=r.buffer?.byteLength!==t;(s||r.buffer!==n)&&(n=s?new SharedArrayBuffer(t):r.buffer,a=new Uint16Array(n),o=new Uint8Array(n),s&&r.buffer&&o.set(new Uint8Array(r.buffer)),r.buffer=n)},r.updateBuffer(),Object.defineProperty(e.writableInstance,r.name,{enumerable:!0,configurable:!0,get(){const t=a[e.index*d];return s.decode(new Uint8Array(o.buffer,e.index*c+2,t))},set(s){const i=t.encode(s);if(i.byteLength>h)throw new Error(`Dynamic string length > ${h} after encoding: ${s}`);a[e.index*d]=i.byteLength,o.set(i,e.index*c+2)}}),Object.defineProperty(e.readonlyInstance,r.name,{enumerable:!0,configurable:!0,get(){const t=a[e.index*d];return s.decode(new Uint8Array(o.buffer,e.index*c+2,t))},set(t){i(e)}})}defineFixed(e,r){const n=this.maxUtf8Length,a=this.lengthsStride,o=this.bytesStride,h=e.capacity*(this.maxUtf8Length+Uint16Array.BYTES_PER_ELEMENT),d=new SharedArrayBuffer(h),c=new Uint16Array(d),l=new Uint8Array(d);r.buffer=d,Object.defineProperty(e.writableInstance,r.name,{enumerable:!0,configurable:!0,get(){const t=c[e.index*a];return s.decode(new Uint8Array(l.buffer,e.index*o+2,t))},set(s){const i=t.encode(s);if(i.byteLength>n)throw new Error(`Dynamic string length > ${n} after encoding: ${s}`);c[e.index*a]=i.byteLength,l.set(i,e.index*o+2)}}),Object.defineProperty(e.readonlyInstance,r.name,{enumerable:!0,configurable:!0,get(){const t=c[e.index*a];return s.decode(new Uint8Array(l.buffer,e.index*o+2,t))},set(t){i(e)}})}}r.boolean=new class extends r{constructor(){super(!1)}defineElastic(e,t){let s,r;t.updateBuffer=()=>{const i=t.buffer?.byteLength!==e.capacity;(i||t.buffer!==s)&&(s=i?new SharedArrayBuffer(e.capacity):t.buffer,r=new Uint8Array(s),i&&t.buffer&&r.set(new Uint8Array(t.buffer)),t.buffer=s)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(r[e.index]),set(t){r[e.index]=t?1:0}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(r[e.index]),set(t){i(e)}})}defineFixed(e,t){const s=new SharedArrayBuffer(e.capacity),r=new Uint8Array(s);t.buffer=s,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(r[e.index]),set(t){r[e.index]=t?1:0}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(r[e.index]),set(t){i(e)}})}},r.uint8=new n(Uint8Array),r.int8=new n(Int8Array),r.uint16=new n(Uint16Array),r.int16=new n(Int16Array),r.uint32=new n(Uint32Array),r.int32=new n(Int32Array),r.float32=new n(Float32Array),r.float64=new n(Float64Array),r.staticString=e=>new a(e),r.dynamicString=e=>new o(e),r.ref=new class extends r{constructor(){super(null)}defineElastic(e,t){let s,r;t.updateBuffer=()=>{const i=e.capacity*Int32Array.BYTES_PER_ELEMENT,n=t.buffer?.byteLength!==i;(n||t.buffer!==s)&&(s=n?new SharedArrayBuffer(i):t.buffer,r=new Int32Array(s),n&&t.buffer&&r.set(new Int32Array(t.buffer)),t.buffer=s)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=r[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){const s=r[e.index],i=t?.__id??-1;s!==i&&(r[e.index]=i)}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=r[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){i(e)}})}defineFixed(e,t){const s=e.capacity*Int32Array.BYTES_PER_ELEMENT,r=new SharedArrayBuffer(s),n=new Int32Array(r);t.buffer=r,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=n[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){const s=n[e.index],i=t?.__id??-1;s!==i&&(n[e.index]=i)}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=n[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){i(e)}})}},r.object=new class extends r{constructor(){super(void 0)}defineElastic(e,t){const s=[];t.localBuffer=s,t.updateBuffer=()=>{},Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>s[e.index],set(t){s[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>s[e.index],set(t){i(e)}})}defineFixed(e,t){const s=new Array(e.capacity);t.localBuffer=s,t.updateBuffer=()=>{},Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>s[e.index],set(t){s[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>s[e.index],set(t){i(e)}})}},r.weakObject=new class extends r{finalizers;constructor(){super(void 0)}defineElastic(e,t){const s=[];t.localBuffer=s,t.updateBuffer=()=>{};const r=this.initFinalizers(e);Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=s[e.index];return null==t?t:t.deref()},set(i){if(null!=i){const s=new WeakRef(i);r?.register(i,{type:e.type,field:t,weakRef:s,id:e.entityId,index:e.index}),i=s}s[e.index]=i}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=s[e.index];return null==t?t:t.deref()},set(t){i(e)}})}defineFixed(e,t){const s=new Array(e.capacity);t.localBuffer=s,t.updateBuffer=()=>{};const r=this.initFinalizers(e);Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=s[e.index];return null==t?t:t.deref()},set(i){if(null!=i){const s=new WeakRef(i);r?.register(i,{type:e.type,field:t,weakRef:s,id:e.entityId,index:e.index}),i=s}s[e.index]=i}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=s[e.index];return null==t?t:t.deref()},set(t){i(e)}})}initFinalizers(e){if(!e.trackedWrites)return;if(this.finalizers)return this.finalizers;const t=e.dispatcher;return t.writeLog&&"undefined"!=typeof FinalizationRegistry?(this.finalizers=new FinalizationRegistry((({type:e,field:s,weakRef:i,id:r,index:n})=>{s.localBuffer?.[n]===i&&t.registry.trackWrite(r,e)})),this.finalizers):void 0}};class h{type;fields;dispatcher;capacity;readonlyInstance;writableInstance;flagOffset;flagMask;trackedWrites;entityId=0;index=0;constructor(e,t,s,i){this.type=e,this.fields=t,this.dispatcher=s,this.capacity=i,this.readonlyInstance=new e,this.writableInstance=new e,this.flagOffset=e.id>>5,this.flagMask=1<<(31&e.id)}}class d{maxEntities;binding;fields;elastic;index;spares;constructor(e,t,s,i){this.maxEntities=e,this.binding=t,this.fields=s,this.elastic=i,this.growSpares(),this.growCapacity()}acquireIndex(e){let t=this.index[e];if(-1===t){if(this.spares[3]>0)t=this.spares[4+--this.spares[3]];else{if(this.spares[1]===this.spares[2]){if(!this.elastic)throw new Error(`Storage exhausted for component ${this.binding.type.name}; raise its capacity above ${this.binding.capacity}`);this.binding.capacity=Math.min(this.maxEntities,2*this.binding.capacity),this.growCapacity()}t=this.spares[1]++}this.index[e]=t}return t}releaseIndex(e){if(-1===this.index[e])throw new Error(`Internal error, index for entity ${e} not allocated`);this.spares[3]===this.spares.length-4&&this.growSpares(),this.spares[4+this.spares[3]++]=this.index[e],this.index[e]=-1}growCapacity(){this.binding.dispatcher.stats.for(this.binding.type).capacity=this.binding.capacity;const e=this.ArrayType,t=e.BYTES_PER_ELEMENT!==this.spares?.[0];if(!this.index||t){const t=new e(new SharedArrayBuffer(this.maxEntities*e.BYTES_PER_ELEMENT));this.index?t.set(this.index):t.fill(-1),this.index=t}if(this.spares&&t){const t=new e(new SharedArrayBuffer(this.spares.length*e.BYTES_PER_ELEMENT));t.set(this.spares),t[0]=e.BYTES_PER_ELEMENT,this.spares=t}if(this.spares[2]=this.binding.capacity,this.elastic)for(const e of this.fields)e.updateBuffer()}growSpares(){const e=this.ArrayType,t=this.spares?Math.min(this.maxEntities,2*(this.spares.length-4)):8,s=new e(new SharedArrayBuffer((4+t)*e.BYTES_PER_ELEMENT));this.spares?s.set(this.spares):(s[0]=e.BYTES_PER_ELEMENT,s[2]=this.binding.capacity),this.spares=s}get ArrayType(){const e=this.binding.capacity;return e<=127?Int8Array:e<=32767?Int16Array:Int32Array}}function c(e,t,s){const i=t.options?.storage??s.defaultComponentStorage,n="sparse"===i?s.maxEntities:Math.min(s.maxEntities,t.options?.capacity??0),a=t.options?.initialCapacity??8;if(void 0!==t.options?.capacity){if("sparse"===i)throw new Error(`Component type ${t.name} cannot combine custom capacity with sparse storage`);if(t.options.capacity<=0)throw new Error(`Component type ${t.name} capacity option must be great than zero: got ${n}`);if(void 0!==t.options.initialCapacity)throw new Error(`Component type ${t.name} cannot have both capacity and initialCapacity options`)}if(("undefined"==typeof process||"test"!==process.env.NODE_ENV)&&t.__bind)throw new Error(`Component type ${t.name} is already in use in another world`);t.id=e;const o=new h(t,function(e){const t=e.schema,s=[];for(const e in t){const i=t[e];let n;n=i instanceof r?{name:e,default:i.defaultValue,type:i}:Object.assign({name:e,default:i.type.defaultValue},i),s.push(n)}return s}(t),s,n||a);t.__binding=o;for(const e of o.fields)n?e.type.defineFixed(o,e):e.type.defineElastic(o,e);switch(i){case"sparse":s.stats.for(t).capacity=n,t.__bind=(e,t)=>(o.entityId=e,o.index=e,t?o.writableInstance:o.readonlyInstance),t.__create=e=>(o.entityId=e,o.index=e,o.writableInstance);break;case"packed":{const e=new d(s.maxEntities,o,o.fields,!n);t.__bind=(s,i)=>{if(o.entityId=s,o.index=e.index[s],-1===o.index)throw new Error(`Attempt to bind unacquired entity ${s} to ${t.name}`);return i?o.writableInstance:o.readonlyInstance},t.__create=t=>(o.entityId=t,o.index=e.acquireIndex(t),o.writableInstance),t.__delete=t=>{e.releaseIndex(t)};break}case"compact":throw new Error("Not yet implemented");default:throw new Error(`Invalid storage type "${i}`)}}const l=22,u=4194303,f=u,m=Object.freeze({});class g{__registry;__id;joined;constructor(e){this.__registry=e}__reset(e){this.__id=e,this.joined=m}add(e,t){if(this.__checkMask(e,!0),this.__registry.hasFlag(this.__id,e))throw new Error(`Entity already has a ${e.name} component`);return this.__registry.setFlag(this.__id,e),this.__registry.dispatcher.stats.for(e).numEntities+=1,function(e,t,s){if(void 0!==s)for(const t in s)if(!e.schema?.[t])throw new Error(`Property ${t} not defined for component ${e.name}`);const i=e.__create(t);for(const t of e.__binding.fields)i[t.name]=s?.[t.name]??t.default}(e,this.__id,t),this}addAll(...e){for(let t=0;t<e.length;t++){const s=e[t];if("function"!=typeof s)throw new Error(`Bad arguments to bulk add: expected component type, got: ${s}`);let i=e[t+1];"function"==typeof i?i=void 0:t++,this.add(s,i)}return this}remove(e){if(!this.has(e))throw new Error(`Entity doesn't have a ${e.name} component`);this.__remove(e)}removeAll(...e){for(const t of e)this.remove(t)}has(e,t=!1){return this.__checkMask(e,!1),this.__registry.hasFlag(this.__id,e,t)}read(e){if(!this.has(e))throw new Error(`Entity doesn't have a ${e.name} component`);return e.__bind(this.__id,!1)}readRecentlyRemoved(e){if(!this.has(e,!0))throw new Error(`Entity doesn't have a ${e.name} component`);return e.__bind(this.__id,!1)}write(e){if(!this.has(e,!0))throw new Error(`Entity doesn't have a ${e.name} component`);return e.__binding.trackedWrites&&this.__registry.trackWrite(this.__id,e),e.__bind(this.__id,!0)}delete(){for(const e of this.__registry.types)this.__registry.hasFlag(this.__id,e)&&(this.__checkMask(e,!0),this.__remove(e));this.__registry.queueDeletion(this.__id),this.__wipeInboundRefs()}__remove(e){this.__deindexOutboundRefs(e),e.__delete&&this.__registry.queueRemoval(this.__id,e),this.__registry.clearFlag(this.__id,e),this.__registry.dispatcher.stats.for(e).numEntities-=1}__deindexOutboundRefs(e){const t=e.__binding.fields;if(t.some((e=>e.type===r.ref))){const s=this.write(e);for(const e of t)e.type===r.ref&&(s[e.name]=null)}}__wipeInboundRefs(){}__checkMask(e,t){const s=this.__registry.executingSystem?.rwMasks,i=t?s?.write:s?.read;if(i&&!this.__registry.maskHasFlag(i,e))throw new Error(`System didn't mark component ${e.name} as ${t?"writable":"readable"}`)}}class p{size;bytes;constructor(e){this.size=e,this.bytes=new Uint32Array(Math.ceil(e/32))}get(e){if(e<0||e>=this.size)throw new Error(`Bit index out of bounds: ${e}`);return 0!=(this.bytes[e>>>5]&1<<(31&e))}set(e){if(e<0||e>=this.size)throw new Error(`Bit index out of bounds: ${e}`);this.bytes[e>>>5]|=1<<(31&e)}unset(e){if(e<0||e>=this.size)throw new Error(`Bit index out of bounds: ${e}`);this.bytes[e>>>5]&=~(1<<(31&e))}clear(){this.bytes.fill(0)}}const y=[];class _{maxEntries;configParamName;data;corral;constructor(e,t){this.maxEntries=e,this.configParamName=t;const s=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.data=new Uint32Array(s);const i=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.corral=new Uint32Array(i)}push(e){const t=this.corral[0];t>=this.maxEntries&&this.throwCapacityExceeded(),t&&this.corral[t]===e||(this.corral[t+2]=e,this.corral[0]+=1)}commit(){const e=this.corral[0];if(!e)return;let t=this.data[0];const s=Math.min(e,this.maxEntries-t);for(this.data.set(this.corral.subarray(2,s+2),t+2),s<e&&this.data.set(this.corral.subarray(s+2,e+2),2),t+=e;t>=this.maxEntries;)t-=this.maxEntries,this.data[1]+=1;this.data[0]=t,this.corral[0]=0,this.corral[1]+=1}createPointer(e){return e?(e.index=this.data[0],e.generation=this.data[1],e.corralIndex=this.corral[0],e.corralGeneration=this.corral[1],e):{index:this.data[0],generation:this.data[1],corralIndex:this.corral[0],corralGeneration:this.corral[1]}}hasUpdatesSince(e){return this.checkPointer(e),!(e.index===this.data[0]&&e.generation===this.data[1]&&(e.corralGeneration===this.corral[1]?e.corralIndex===this.corral[0]:0===this.corral[0]))}processSince(e,t){this.checkPointers(e,t);let s=y;const i=t?.index??this.data[0],r=t?.generation??this.data[1];if(e.generation===r)if(e.index<i)s=[this.data,e.index+2,i+2],e.index=i;else{const t=this.corral[0],i=this.corral[1];(e.corralGeneration===i?e.corralIndex<t:t)&&(s=[this.corral,e.corralIndex+2,t+2],e.corralIndex=t,e.corralGeneration=i)}else s=[this.data,e.index+2,this.data.length],e.index=0,e.generation=r;return s}countSince(e,t){if(this.checkPointers(e,t),this.corral[0])throw new Error("Internal error, should commit log before counting");const s=e.index,i=t?.index??this.data[0],r=t?.generation??this.data[1];return e.index=i,e.generation=r,s===i&&e.generation===r?0:s<i?i-s:this.maxEntries-(s-i)}checkPointers(e,t){if(this.checkPointer(e),t&&(this.checkPointer(t),e.index>t.index&&e.generation>=t.generation))throw new RangeError("Internal error, start pointer exceeds end pointer")}checkPointer(e){const t=this.data[0];let s=e.generation;if(e.index===t?s+1<this.data[1]&&this.throwCapacityExceeded():(e.index>t&&(s+=1),s!==this.data[1]&&this.throwCapacityExceeded()),e.corralGeneration>this.corral[1])throw new Error("Internal error, pointer corral generation older than corral");if(e.corralGeneration===this.corral[1]&&e.corralIndex>this.corral[0])throw new Error("Internal error, pointer past end of log corral area")}throwCapacityExceeded(){throw new Error(`Log capacity exceeded, please raise ${this.configParamName} above ${this.maxEntries}`)}}class w{maxItems;configParamName;data;constructor(e,t){this.maxItems=e,this.configParamName=t,this.data=new Uint32Array(new SharedArrayBuffer((e+1)*Uint32Array.BYTES_PER_ELEMENT))}get length(){return this.data[0]}take(){const e=this.data[0]--;if(e<=0)throw new RangeError(`Pool capacity exceeded, please raise ${this.configParamName} above ${this.maxItems}`);return this.data[e]}refill(e){if(!e.length)return;const t=this.length,s=t+e.length;if(s>this.maxItems)throw new Error("Internal error, refill exceeded pool capacity");this.data.set(e,t+1),this.data[0]=s}fillWithDescendingIntegers(e){const t=this.length;for(let s=this.data.length-1;s>t;s--)this.data[s]=e++;this.data[0]=this.data.length-1}}class b{pool;entities=[];constructor(e){this.pool=e}add(e){this.entities.push(this.pool.borrowTemporarily(e))}clear(){this.entities.length&&this.entities.splice(0,1/0)}}class x{pool;entities=[];lookupTable;constructor(e,t){this.pool=e,this.lookupTable=new Int32Array(t),this.lookupTable.fill(-1)}add(e){const t=this.entities.push(this.pool.borrow(e))-1;this.lookupTable[e]=t}remove(e){const t=this.lookupTable[e];if(t<0)throw new Error("Internal error, entity not in list");this.pool.return(e),this.lookupTable[e]=-1;const s=this.entities.pop();t<this.entities.length&&(this.entities[t]=s,this.lookupTable[s.__id]=t)}has(e){return this.lookupTable[e]>=0}clear(){throw new Error("Internal error, trying to clear persistent entity list")}}var E;!function(e){e[e.all=1]="all",e[e.added=2]="added",e[e.removed=4]="removed",e[e.changed=8]="changed",e[e.addedOrChanged=16]="addedOrChanged",e[e.changedOrRemoved=32]="changedOrRemoved",e[e.addedChangedOrRemoved=64]="addedChangedOrRemoved"}(E||(E={}));const v=E.added|E.removed|E.changed|E.addedOrChanged|E.changedOrRemoved|E.addedChangedOrRemoved,L=E.changed|E.addedOrChanged|E.changedOrRemoved|E.addedChangedOrRemoved;class P{query;system;results={};flavors=0;__withMask;__withoutMask;__trackMask;__refMask;hasTransientResults;hasChangedResults;currentEntities;changedEntities;constructor(e,t){this.query=e,this.system=t,e.__results=this.results,e.__systemName=t.name}complete(){const e=this.system.dispatcher;if(this.hasTransientResults=Boolean(this.flavors&v),this.hasChangedResults=Boolean(this.flavors&L),this.hasChangedResults&&!this.__trackMask)throw new Error("Query for changed entities must track at least one component");this.flavors&E.all?this.results.all=new x(e.registry.pool,e.maxEntities):this.currentEntities=new p(e.maxEntities),this.hasTransientResults&&this.allocateTransientResultLists(),this.flavors&&this.system.shapeQueries.push(this),this.hasChangedResults&&(this.changedEntities=new p(e.maxEntities),this.system.writeQueries.push(this))}allocateTransientResultLists(){this.flavors&E.added&&this.allocateResult("added"),this.flavors&E.removed&&this.allocateResult("removed"),this.flavors&E.changed&&this.allocateResult("changed"),this.flavors&E.addedOrChanged&&this.allocateResult("addedOrChanged"),this.flavors&E.changedOrRemoved&&this.allocateResult("changedOrRemoved"),this.flavors&E.addedChangedOrRemoved&&this.allocateResult("addedChangedOrRemoved")}allocateResult(e){const t=this.system.dispatcher;this.results[e]=new b(t.registry.pool)}clearTransientResults(){this.hasTransientResults&&(this.results.added?.clear(),this.results.removed?.clear(),this.results.changed?.clear(),this.results.addedOrChanged?.clear(),this.results.changedOrRemoved?.clear(),this.results.addedChangedOrRemoved?.clear(),this.changedEntities?.clear())}handleShapeUpdate(e){const t=this.system.dispatcher.registry,s=this.results.all?.has(e)??this.currentEntities.get(e),i=t.matchShape(e,this.__withMask,this.__withoutMask);i&&!s?(this.currentEntities?.set(e),this.results.all?.add(e),this.results.added?.add(e),this.results.addedOrChanged?.add(e),this.results.addedChangedOrRemoved?.add(e)):!i&&s&&(this.currentEntities?.unset(e),this.results.all?.remove(e),this.results.removed?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}handleWrite(e,t,s){!this.changedEntities.get(e)&&(this.__trackMask[t]??0)&s&&(this.changedEntities.set(e),this.results.changed?.add(e),this.results.addedOrChanged?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}}class S{__callback;__userQuery;__query;__system;__lastTypes;constructor(e,t){this.__callback=e,this.__userQuery=t}__build(e){try{this.__system=e,this.__query=new P(this.__userQuery,e),this.__callback(this),this.__query.complete()}catch(t){throw t.message=`Failed to build query in system ${e.name}: ${t.message}`,t}}get and(){return this}get but(){return this}get also(){return this}get all(){return this.__query.flavors|=E.all,this}get added(){return this.__query.flavors|=E.added,this}get removed(){return this.__query.flavors|=E.removed,this}get changed(){return this.__query.flavors|=E.changed,this}get addedOrChanged(){return this.__query.flavors|=E.addedOrChanged,this}get changedOrRemoved(){return this.__query.flavors|=E.changedOrRemoved,this}get addedChangedOrRemoved(){return this.__query.flavors|=E.addedChangedOrRemoved,this}with(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withMask"),this}without(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withoutMask",e),this}using(...e){return this.set(this.__system.rwMasks.read,e),this}get track(){this.set("__trackMask");for(const e of this.__lastTypes)e.__binding.trackedWrites=!0;return this}get read(){return this}get write(){return this.set(this.__system.rwMasks.write),this}set(e,t,s){if(e){if(t||(t=this.__lastTypes),!t)throw new Error("No component type to apply query modifier to");if(this.__lastTypes=t,"string"==typeof e){if(s&&this.__query[e])throw new Error(`Only one ${s} allowed`);this.__query[e]||(this.__query[e]=[]),e=this.__query[e]}else if(s&&e.some((e=>0!==e)))throw new Error(`Only one ${s} allowed`);for(const s of t)this.__system.dispatcher.registry.extendMaskAndSetFlag(e,s)}}}class I{__results;__systemName;get all(){return this.__checkList("all"),this.__results.all.entities}get added(){return this.__checkList("added"),this.__results.added.entities}get removed(){return this.__checkList("removed"),this.__results.removed.entities}get changed(){return this.__checkList("changed"),this.__results.changed.entities}get addedOrChanged(){return this.__checkList("addedOrChanged"),this.__results.addedOrChanged.entities}get changedOrRemoved(){return this.__checkList("changedOrRemoved"),this.__results.changedOrRemoved.entities}get addedChangedOrRemoved(){return this.__checkList("addedChangedOrRemoved"),this.__results.addedChangedOrRemoved.entities}__checkList(e){if(!this.__results[e])throw new Error(`Query '${e}' not configured, please add .${e} to your query definition in system ${this.__systemName}`)}}class R{__queryBuilders=[];__dispatcher;time;delta;get name(){return this.constructor.name}query(e){const t=new I,s=new S(e,t);if(!this.__queryBuilders)throw new Error(`Attempt to create a new query after world initialized in system ${this.name}`);return this.__queryBuilders.push(s),t}createEntity(...e){return this.__dispatcher.createEntity(e)}}class k{system;dispatcher;rwMasks={read:[],write:[]};shapeQueries=[];writeQueries=[];hasWriteQueries;processedEntities;shapeLogPointer;writeLogPointer;get name(){return this.system.name}constructor(e,t){this.system=e,this.dispatcher=t,e.__dispatcher=t,this.shapeLogPointer=t.shapeLog.createPointer(),this.writeLogPointer=t.writeLog?.createPointer(),this.processedEntities=new p(t.maxEntities);for(const t of e.__queryBuilders)t.__build(this);e.__queryBuilders=null,this.hasWriteQueries=!!this.writeQueries.length}execute(e,t){this.system.time=e,this.system.delta=t,this.runQueries(),this.system.execute()}runQueries(){const e=this.dispatcher.shapeLog.hasUpdatesSince(this.shapeLogPointer),t=this.hasWriteQueries&&this.dispatcher.writeLog.hasUpdatesSince(this.writeLogPointer);if(e||t){this.processedEntities.clear();for(const e of this.shapeQueries)e.clearTransientResults();e&&this.__updateShapeQueries(),t&&this.__updateWriteQueries()}}__updateShapeQueries(){const e=this.dispatcher.shapeLog;let t,s,i;for(;[t,s,i]=e.processSince(this.shapeLogPointer),t;)for(let e=s;e<i;e++){const s=t[e];if(!this.processedEntities.get(s)){this.processedEntities.set(s);for(const e of this.shapeQueries)e.handleShapeUpdate(s)}}}__updateWriteQueries(){const e=this.dispatcher.writeLog;let t,s,i;for(;[t,s,i]=e.processSince(this.writeLogPointer),t;)for(let e=s;e<i;e++){const s=t[e],i=s&f;if(!this.processedEntities.get(i)){const e=s>>>l;for(const t of this.writeQueries)t.handleWrite(i,e>>5,1<<(31&e))}}}}class A{registry;borrowed;borrowCounts;spares=[];temporarilyBorrowedIds=[];constructor(e,t){this.registry=e,this.borrowed=Array.from({length:t}),this.borrowCounts=new Int32Array(t)}borrow(e){this.borrowCounts[e]+=1;let t=this.borrowed[e];return t||(t=this.borrowed[e]=this.spares.pop()??new g(this.registry),t.__reset(e)),t}borrowTemporarily(e){const t=this.borrow(e);return this.temporarilyBorrowedIds.push(e),t}returnTemporaryBorrows(){for(const e of this.temporarilyBorrowedIds)this.return(e);this.temporarilyBorrowedIds.splice(0,1/0)}return(e){if(!this.borrowCounts[e])throw new Error("Internal error, returning entity with no borrows");--this.borrowCounts[e]<=0&&(this.spares.push(this.borrowed[e]),this.borrowed[e]=void 0)}}class O{types;dispatcher;stride;shapes;staleShapes;entityIdPool;pool;executingSystem;deletionLog;prevDeletionPointer;oldDeletionPointer;removalLog;prevRemovalPointer;oldRemovalPointer;constructor(e,t,s,i,r){this.types=i,this.dispatcher=r;let n=0;for(const e of i)c(n++,e,this.dispatcher);this.stride=Math.ceil(i.length/32);const a=e*this.stride*4;this.shapes=new Uint32Array(new SharedArrayBuffer(a)),this.staleShapes=new Uint32Array(new SharedArrayBuffer(a)),this.entityIdPool=new w(e,"maxEntities"),this.entityIdPool.fillWithDescendingIntegers(0),this.pool=new A(this,e),this.deletionLog=new _(t,"maxLimboEntities"),this.prevDeletionPointer=this.deletionLog.createPointer(),this.oldDeletionPointer=this.deletionLog.createPointer(),this.removalLog=new _(s,"maxLimboComponents"),this.prevRemovalPointer=this.removalLog.createPointer(),this.oldRemovalPointer=this.removalLog.createPointer()}createEntity(e){const t=this.entityIdPool.take();this.shapes.fill(0,t*this.stride,(t+1)*this.stride);const s=this.pool.borrowTemporarily(t);return e&&s.addAll(...e),this.dispatcher.stats.numEntities+=1,s}queueDeletion(e){this.deletionLog.push(e)}queueRemoval(e,t){this.removalLog.push(e|t.id<<l)}flush(){this.pool.returnTemporaryBorrows(),this.deletionLog.commit(),this.removalLog.commit()}processEndOfFrame(){this.processDeletionLog(),this.processRemovalLog()}processDeletionLog(){this.deletionLog.commit();let e,t,s,i=0;for(;[e,t,s]=this.deletionLog.processSince(this.oldDeletionPointer,this.prevDeletionPointer),e;){const r=e.subarray(t,s);this.entityIdPool.refill(r),i+=r.length}this.dispatcher.stats.numEntities-=i,this.dispatcher.stats.maxLimboEntities=i,this.deletionLog.createPointer(this.prevDeletionPointer)}processRemovalLog(){this.removalLog.commit();let e,t,s,i=0;for(;[e,t,s]=this.removalLog.processSince(this.oldRemovalPointer,this.prevRemovalPointer),e;){for(let i=t;i<s;i++){const t=e[i],s=t&f,r=t>>>l,n=this.types[r],a=s*this.stride+n.__binding.flagOffset,o=n.__binding.flagMask;0==(this.shapes[a]&o)&&(this.staleShapes[a]&=~o,n.__delete(s))}i+=s-t}this.dispatcher.stats.maxLimboComponents=i,this.removalLog.createPointer(this.prevRemovalPointer)}extendMaskAndSetFlag(e,t){const s=t.__binding.flagOffset;s>=e.length&&(e.length=s+1,e.fill(0,e.length,s)),e[s]|=t.__binding.flagMask}maskHasFlag(e,t){return 0!=((e?.[t.__binding.flagOffset]??0)&t.__binding.flagMask)}hasFlag(e,t,s=!1){const i=e*this.stride+t.__binding.flagOffset,r=t.__binding.flagMask;return 0!=(this.shapes[i]&r)||!(!s||0==(this.staleShapes[i]&r))}setFlag(e,t){const s=e*this.stride+t.__binding.flagOffset,i=t.__binding.flagMask;this.shapes[s]|=i,this.staleShapes[s]|=i,this.dispatcher.shapeLog.push(e)}clearFlag(e,t){this.shapes[e*this.stride+t.__binding.flagOffset]&=~t.__binding.flagMask,this.dispatcher.shapeLog.push(e)}trackWrite(e,t){this.dispatcher.writeLog.push(e|t.id<<l)}matchShape(e,t,s){const i=e*this.stride;if(t)for(let e=0;e<t.length;e++){const s=t[e];if((this.shapes[i+e]&s)!==s)return!1}if(s)for(let e=0;e<s.length;e++){const t=s[e];if(0!=(this.shapes[i+e]&t))return!1}return!0}}class T{_numEntities=0;maxEntities=0;capacity=0;get numEntities(){return this._numEntities}set numEntities(e){this._numEntities=e,e>this.maxEntities&&(this.maxEntities=e)}toString(){return`${this.numEntities.toLocaleString()} of ${this.maxEntities.toLocaleString()} peak (capacity ${this.capacity.toLocaleString()})`}}class C{frames=0;_numEntities=0;maxEntities=0;_maxLimboEntities=0;_maxLimboComponents=0;_numRefs=0;maxRefs=0;_maxShapeChangesPerFrame=0;_maxWritesPerFrame=0;components=Object.create(null);get numEntities(){return this._numEntities}set numEntities(e){this._numEntities=e,e>this.maxEntities&&(this.maxEntities=e)}get maxLimboEntities(){return this._maxLimboEntities}set maxLimboEntities(e){e>this._maxLimboEntities&&(this._maxLimboEntities=e)}get maxLimboComponents(){return this._maxLimboComponents}set maxLimboComponents(e){e>this._maxLimboComponents&&(this._maxLimboComponents=e)}get numRefs(){return this._numRefs}set numRefs(e){this._numRefs=e,e>this.maxRefs&&(this.maxRefs=e)}get maxShapeChangesPerFrame(){return this._maxShapeChangesPerFrame}set maxShapeChangesPerFrame(e){e>this._maxShapeChangesPerFrame&&(this._maxShapeChangesPerFrame=e)}get maxWritesPerFrame(){return this._maxWritesPerFrame}set maxWritesPerFrame(e){e>this._maxWritesPerFrame&&(this._maxWritesPerFrame=e)}for(e){return this.components[e.name]=this.components[e.name]??new T}toString(){return`World stats:\n frames: ${this.frames.toLocaleString()}\n entities: ${this.numEntities.toLocaleString()} of ${this.maxEntities.toLocaleString()} max (${this.maxLimboEntities.toLocaleString()} limbo max)\n refs: ${this.numRefs.toLocaleString()} of ${this.maxRefs.toLocaleString()} max\n logs: ${this.maxShapeChangesPerFrame.toLocaleString()} shape changes/frame max, ${this.maxWritesPerFrame.toLocaleString()} writes/frame max`}}const B="undefined"!=typeof window&&void 0!==window.performance?performance.now.bind(performance):Date.now.bind(Date);class F extends R{__callback;execute(){this.__callback(this)}}class M{maxEntities;defaultComponentStorage;registry;systems;lastTime=B()/1e3;executing;shapeLog;writeLog;shapeLogFramePointer;writeLogFramePointer;stats;userCallbackSystem;callbackSystem;constructor({defs:e,maxEntities:t=1e4,maxLimboEntities:s=Math.ceil(t/5),maxLimboComponents:i=Math.ceil(t/5),maxRefs:r=t,maxShapeChangesPerFrame:n=2*t,maxWritesPerFrame:a=4*t,defaultComponentStorage:o="sparse"}){if(t>u)throw new Error("maxEntities too high, the limit is 4194303");const{componentTypes:h,systemTypes:d}=this.splitDefs(e);if(h.length>1024)throw new Error("Too many component types, the limit is 1024");this.stats=new C,this.maxEntities=t,this.defaultComponentStorage=o,this.shapeLog=new _(n,"maxShapeChangesPerFrame"),this.shapeLogFramePointer=this.shapeLog.createPointer(),this.registry=new O(t,s,i,h.flat(1/0),this),this.systems=this.normalizeAndInitSystems(d),this.systems.some((e=>e.hasWriteQueries))&&(this.writeLog=new _(a,"maxWritesPerFrame"),this.writeLogFramePointer=this.writeLog.createPointer()),this.userCallbackSystem=new F,this.callbackSystem=new k(this.userCallbackSystem,this)}normalizeAndInitSystems(e){const t=[],s=e.flat(1/0);for(let e=0;e<s.length;e++){const i=new s[e],r=s[e+1];r&&"function"!=typeof r&&(Object.assign(i,r),e++),t.push(new k(i,this))}return t}splitDefs(e){const t=[],s=[];let i=!1;for(const r of e.flat(1/0))if("function"==typeof r)i=!r.schema,(i?s:t).push(r);else{if(!i)throw new Error("Unexpected value in world defs: "+r);s.push(r),i=!1}return{componentTypes:t,systemTypes:s}}execute(e,t,s){if(this.executing)throw new Error("Recursive system execution not allowed");this.executing=!0,void 0===e&&(e=B()/1e3),void 0===t&&(t=e-this.lastTime),this.lastTime=e;for(const i of s??this.systems)this.registry.executingSystem=i,i.execute(e,t),this.flush();this.registry.executingSystem=void 0,this.registry.processEndOfFrame(),this.executing=!1,this.gatherFrameStats()}executeFunction(e){if(this.executing)throw new Error("Ad hoc function execution not allowed while world is executing");this.executing=!0,this.userCallbackSystem.__callback=e,this.callbackSystem.execute(0,0),this.flush(),this.registry.processEndOfFrame(),this.executing=!1,this.gatherFrameStats()}gatherFrameStats(){this.stats.frames+=1,this.stats.maxShapeChangesPerFrame=this.shapeLog.countSince(this.shapeLogFramePointer),this.stats.maxWritesPerFrame=this.writeLog?.countSince(this.writeLogFramePointer)??0}flush(){this.registry.flush(),this.shapeLog.commit(),this.writeLog?.commit()}createEntity(e){const t=this.registry.createEntity(e);return this.executing||this.flush(),t}}const $=[];e.Entity=g,e.Query=I,e.System=R,e.Type=r,e.World=class{__dispatcher;constructor(e){this.__dispatcher=new M(e)}build(e){this.__dispatcher.executeFunction(e)}createEntity(...e){this.__dispatcher.createEntity(e)}execute(e,t){this.__dispatcher.execute(e,t)}get stats(){return this.__dispatcher.stats}},e.component=function(e){if("function"!=typeof e)return t=>{t.options=e,$.push(t)};$.push(e)},e.componentTypes=$,e.prop=function(e){return function(t,s){t.constructor.schema||(t.constructor.schema={});const i="type"in e?e:{type:e};t.constructor.schema[s]=i}},Object.defineProperty(e,"__esModule",{value:!0})})); | ||
//# sourceMappingURL=index.umd.min.js.map |
{ | ||
"name": "@lastolivegames/becsy", | ||
"type": "module", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"scripts": { | ||
"test": "jest --config jestconfig.json", | ||
"test:watch": "jest --watch --config jestconfig.json", | ||
"bench": "npm run build && env NODE_ENV=test node --expose-gc --enable-source-maps --es-module-specifier-resolution=node lib/benchmarks/index.js", | ||
"bench": "rollup --config rollup.benchmarks.config.mjs && cp index.* perf.* lib && env NODE_ENV=test node --expose-gc --enable-source-maps --es-module-specifier-resolution=node lib/benchmarks/index.js", | ||
"bench:watch": "tsc-watch --onSuccess \"env NODE_ENV=test node --expose-gc --enable-source-maps --es-module-specifier-resolution=node lib/benchmarks/index.js\"", | ||
"deopt": "rm *.log && node --trace-ic --enable-source-maps --es-module-specifier-resolution=node lib/tests/performance.test.js && mv *.log v8.pre.log && node striplog.cjs && deoptigate", | ||
"flame": "clinic flame -- node --enable-source-maps --es-module-specifier-resolution=node lib/tests/performance.test.js", | ||
"build": "rollup --config && cp index.* perf.* lib", | ||
"build": "rollup --config", | ||
"prepublishOnly": "yarn build", | ||
@@ -36,2 +36,3 @@ "lint": "eslint src tests benchmarks" | ||
"@wessberg/rollup-plugin-ts": "^1.3.11", | ||
"chalk": "^4.1.0", | ||
"eslint": "^7.22.0", | ||
@@ -38,0 +39,0 @@ "eslint-plugin-disable": "^2.0.1", |
135
perf.d.ts
@@ -1,38 +0,1 @@ | ||
declare class Indexer { | ||
private readonly maxRefs; | ||
private readonly buffer; | ||
private readonly index; | ||
private numRefs; | ||
constructor(maxRefs: number); | ||
/** | ||
* Inserts a new reference entry into the index, but fails if this exact pair is already indexed. | ||
*/ | ||
insert(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Removes a reference entry from the index, failing if it's missing. | ||
*/ | ||
remove(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Returns an iterable over the IDs of all entities that refer to the given one. | ||
*/ | ||
iterateReferrers(referenceId: EntityId): Iterable<EntityId>; | ||
/** | ||
* Finds the entry that matches referencedId:referrerId exactly. If not found, finds the first | ||
* entry that would follow referenceId:referrerId. If referrerId is not specified, finds the | ||
* first referenceId:* entry, if any. | ||
*/ | ||
private findIndex; | ||
} | ||
/** | ||
* A fixed but arbitrary size bitset. | ||
*/ | ||
declare class Bitset { | ||
private readonly size; | ||
private readonly bytes; | ||
constructor(size: number); | ||
get(index: number): boolean; | ||
set(index: number): void; | ||
unset(index: number): void; | ||
clear(): void; | ||
} | ||
interface LogPointer { | ||
@@ -215,3 +178,2 @@ index: number; | ||
private processedEntities; | ||
removedEntities: Bitset; | ||
private shapeLogPointer; | ||
@@ -226,10 +188,9 @@ private writeLogPointer; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defs: DefsArray; | ||
declare class ComponentStats { | ||
_numEntities: number; | ||
maxEntities: number; | ||
capacity: number; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
toString(): string; | ||
} | ||
@@ -240,22 +201,40 @@ declare class Stats { | ||
maxEntities: number; | ||
_maxLimboEntities: number; | ||
_maxLimboComponents: number; | ||
_numRefs: number; | ||
maxRefs: number; | ||
_maxShapeChangesPerFrame: number; | ||
_maxWritesPerFrame: number; | ||
components: { | ||
[typeName: string]: ComponentStats; | ||
}; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
_maxLimboEntities: number; | ||
get maxLimboEntities(): number; | ||
set maxLimboEntities(value: number); | ||
_numRefs: number; | ||
maxRefs: number; | ||
get maxLimboComponents(): number; | ||
set maxLimboComponents(value: number); | ||
get numRefs(): number; | ||
set numRefs(value: number); | ||
_maxShapeChangesPerFrame: number; | ||
get maxShapeChangesPerFrame(): number; | ||
set maxShapeChangesPerFrame(value: number); | ||
_maxWritesPerFrame: number; | ||
get maxWritesPerFrame(): number; | ||
set maxWritesPerFrame(value: number); | ||
for(type: ComponentType<any>): ComponentStats; | ||
toString(): string; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
defs: DefsArray; | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxLimboComponents?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defaultComponentStorage?: ComponentStorage; | ||
} | ||
declare class Dispatcher { | ||
readonly maxEntities: number; | ||
readonly indexer: Indexer; | ||
readonly defaultComponentStorage: ComponentStorage; | ||
readonly registry: Registry; | ||
@@ -272,3 +251,3 @@ readonly systems: SystemBox[]; | ||
private readonly callbackSystem; | ||
constructor({ defs, maxEntities, maxLimboEntities, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame }: WorldOptions); | ||
constructor({ defs, maxEntities, maxLimboEntities, maxLimboComponents, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame, defaultComponentStorage }: WorldOptions); | ||
private normalizeAndInitSystems; | ||
@@ -299,2 +278,3 @@ private splitDefs; | ||
private readonly shapes; | ||
private readonly staleShapes; | ||
private readonly entityIdPool; | ||
@@ -306,7 +286,13 @@ readonly pool: EntityPool; | ||
private readonly oldDeletionPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
private readonly removalLog; | ||
private readonly prevRemovalPointer; | ||
private readonly oldRemovalPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, maxLimboComponents: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
createEntity(initialComponents: (ComponentType<any> | any)[]): Entity; | ||
queueDeletion(id: EntityId): void; | ||
queueRemoval(id: EntityId, type: ComponentType<any>): void; | ||
flush(): void; | ||
processEndOfFrame(): void; | ||
private processDeletionLog; | ||
private processRemovalLog; | ||
extendMaskAndSetFlag(mask: number[], type: ComponentType<any>): void; | ||
@@ -350,3 +336,4 @@ maskHasFlag(mask: number[] | undefined, type: ComponentType<any>): boolean; | ||
constructor(defaultValue: JSType); | ||
abstract define<C>(binding: Binding<C>, name: string, buffer?: SharedArrayBuffer): SharedArrayBuffer; | ||
abstract defineElastic(binding: Binding<any>, field: Field<any>): void; | ||
abstract defineFixed(binding: Binding<any>, field: Field<any>): void; | ||
static boolean: Type<boolean>; | ||
@@ -364,2 +351,4 @@ static uint8: Type<number>; | ||
static ref: Type<Entity | null>; | ||
static object: Type<any>; | ||
static weakObject: Type<any>; | ||
} | ||
@@ -373,2 +362,8 @@ interface SchemaDef<JSType> { | ||
} | ||
type ComponentStorage = "sparse" | "packed" | "compact"; | ||
interface ComponentOptions { | ||
storage?: ComponentStorage; | ||
capacity?: number; | ||
initialCapacity?: number; | ||
} | ||
interface Field<JSType> { | ||
@@ -378,2 +373,5 @@ name: string; | ||
default: JSType; | ||
buffer?: SharedArrayBuffer; | ||
localBuffer?: any[]; | ||
updateBuffer?(): void; | ||
} | ||
@@ -383,19 +381,27 @@ interface ComponentType<C> { | ||
schema?: Schema; | ||
maxEntities?: number; | ||
__id?: number; | ||
__flagOffset?: number; | ||
__flagMask?: number; | ||
__trackedWrites?: boolean; | ||
__fields?: Field<any>[]; | ||
options?: ComponentOptions; | ||
/** | ||
* A unique, sequential id number for this component type, assigned automatically by becsy. It | ||
* will stay the same across runs as long as the list of defs used to create the world doesn't | ||
* change. Feel free to use this for your own purposes but don't change it. | ||
*/ | ||
id?: number; | ||
__binding?: Binding<C>; | ||
__bind?(id: EntityId, writable: boolean): C; | ||
__create?(id: EntityId): C; | ||
__delete?(id: EntityId): void; | ||
} | ||
declare class Binding<C> { | ||
readonly type: ComponentType<C>; | ||
readonly fields: Field<any>[]; | ||
readonly dispatcher: Dispatcher; | ||
readonly maxEntities: number; | ||
capacity: number; | ||
readonly readonlyInstance: C; | ||
readonly writableInstance: C; | ||
readonly flagOffset: number; | ||
readonly flagMask: number; | ||
trackedWrites: boolean; | ||
entityId: number; | ||
index: number; | ||
constructor(type: ComponentType<C>, dispatcher: Dispatcher, maxEntities: number); | ||
constructor(type: ComponentType<C>, fields: Field<any>[], dispatcher: Dispatcher, capacity: number); | ||
} | ||
@@ -408,3 +414,3 @@ declare class World { | ||
execute(time?: number, delta?: number): void; | ||
get stats(): any; | ||
get stats(): Stats; | ||
} | ||
@@ -418,3 +424,4 @@ interface PropOptions<JSType> { | ||
declare function component(constructor: ComponentType<any>): void; | ||
declare function component(options: ComponentOptions): (constructor: ComponentType<any>) => void; | ||
export { World, Type, Entity, System, SystemType, componentTypes, component, prop, Query, ComponentType }; | ||
//# sourceMappingURL=perf.d.ts.map |
@@ -1,38 +0,1 @@ | ||
declare class Indexer { | ||
private readonly maxRefs; | ||
private readonly buffer; | ||
private readonly index; | ||
private numRefs; | ||
constructor(maxRefs: number); | ||
/** | ||
* Inserts a new reference entry into the index, but fails if this exact pair is already indexed. | ||
*/ | ||
insert(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Removes a reference entry from the index, failing if it's missing. | ||
*/ | ||
remove(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Returns an iterable over the IDs of all entities that refer to the given one. | ||
*/ | ||
iterateReferrers(referenceId: EntityId): Iterable<EntityId>; | ||
/** | ||
* Finds the entry that matches referencedId:referrerId exactly. If not found, finds the first | ||
* entry that would follow referenceId:referrerId. If referrerId is not specified, finds the | ||
* first referenceId:* entry, if any. | ||
*/ | ||
private findIndex; | ||
} | ||
/** | ||
* A fixed but arbitrary size bitset. | ||
*/ | ||
declare class Bitset { | ||
private readonly size; | ||
private readonly bytes; | ||
constructor(size: number); | ||
get(index: number): boolean; | ||
set(index: number): void; | ||
unset(index: number): void; | ||
clear(): void; | ||
} | ||
interface LogPointer { | ||
@@ -215,3 +178,2 @@ index: number; | ||
private processedEntities; | ||
removedEntities: Bitset; | ||
private shapeLogPointer; | ||
@@ -226,10 +188,9 @@ private writeLogPointer; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defs: DefsArray; | ||
declare class ComponentStats { | ||
_numEntities: number; | ||
maxEntities: number; | ||
capacity: number; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
toString(): string; | ||
} | ||
@@ -240,22 +201,40 @@ declare class Stats { | ||
maxEntities: number; | ||
_maxLimboEntities: number; | ||
_maxLimboComponents: number; | ||
_numRefs: number; | ||
maxRefs: number; | ||
_maxShapeChangesPerFrame: number; | ||
_maxWritesPerFrame: number; | ||
components: { | ||
[typeName: string]: ComponentStats; | ||
}; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
_maxLimboEntities: number; | ||
get maxLimboEntities(): number; | ||
set maxLimboEntities(value: number); | ||
_numRefs: number; | ||
maxRefs: number; | ||
get maxLimboComponents(): number; | ||
set maxLimboComponents(value: number); | ||
get numRefs(): number; | ||
set numRefs(value: number); | ||
_maxShapeChangesPerFrame: number; | ||
get maxShapeChangesPerFrame(): number; | ||
set maxShapeChangesPerFrame(value: number); | ||
_maxWritesPerFrame: number; | ||
get maxWritesPerFrame(): number; | ||
set maxWritesPerFrame(value: number); | ||
for(type: ComponentType<any>): ComponentStats; | ||
toString(): string; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
defs: DefsArray; | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxLimboComponents?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defaultComponentStorage?: ComponentStorage; | ||
} | ||
declare class Dispatcher { | ||
readonly maxEntities: number; | ||
readonly indexer: Indexer; | ||
readonly defaultComponentStorage: ComponentStorage; | ||
readonly registry: Registry; | ||
@@ -272,3 +251,3 @@ readonly systems: SystemBox[]; | ||
private readonly callbackSystem; | ||
constructor({ defs, maxEntities, maxLimboEntities, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame }: WorldOptions); | ||
constructor({ defs, maxEntities, maxLimboEntities, maxLimboComponents, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame, defaultComponentStorage }: WorldOptions); | ||
private normalizeAndInitSystems; | ||
@@ -299,2 +278,3 @@ private splitDefs; | ||
private readonly shapes; | ||
private readonly staleShapes; | ||
private readonly entityIdPool; | ||
@@ -306,7 +286,13 @@ readonly pool: EntityPool; | ||
private readonly oldDeletionPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
private readonly removalLog; | ||
private readonly prevRemovalPointer; | ||
private readonly oldRemovalPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, maxLimboComponents: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
createEntity(initialComponents: (ComponentType<any> | any)[]): Entity; | ||
queueDeletion(id: EntityId): void; | ||
queueRemoval(id: EntityId, type: ComponentType<any>): void; | ||
flush(): void; | ||
processEndOfFrame(): void; | ||
private processDeletionLog; | ||
private processRemovalLog; | ||
extendMaskAndSetFlag(mask: number[], type: ComponentType<any>): void; | ||
@@ -350,3 +336,4 @@ maskHasFlag(mask: number[] | undefined, type: ComponentType<any>): boolean; | ||
constructor(defaultValue: JSType); | ||
abstract define<C>(binding: Binding<C>, name: string, buffer?: SharedArrayBuffer): SharedArrayBuffer; | ||
abstract defineElastic(binding: Binding<any>, field: Field<any>): void; | ||
abstract defineFixed(binding: Binding<any>, field: Field<any>): void; | ||
static boolean: Type<boolean>; | ||
@@ -364,2 +351,4 @@ static uint8: Type<number>; | ||
static ref: Type<Entity | null>; | ||
static object: Type<any>; | ||
static weakObject: Type<any>; | ||
} | ||
@@ -373,2 +362,8 @@ interface SchemaDef<JSType> { | ||
} | ||
type ComponentStorage = "sparse" | "packed" | "compact"; | ||
interface ComponentOptions { | ||
storage?: ComponentStorage; | ||
capacity?: number; | ||
initialCapacity?: number; | ||
} | ||
interface Field<JSType> { | ||
@@ -378,2 +373,5 @@ name: string; | ||
default: JSType; | ||
buffer?: SharedArrayBuffer; | ||
localBuffer?: any[]; | ||
updateBuffer?(): void; | ||
} | ||
@@ -383,19 +381,27 @@ interface ComponentType<C> { | ||
schema?: Schema; | ||
maxEntities?: number; | ||
__id?: number; | ||
__flagOffset?: number; | ||
__flagMask?: number; | ||
__trackedWrites?: boolean; | ||
__fields?: Field<any>[]; | ||
options?: ComponentOptions; | ||
/** | ||
* A unique, sequential id number for this component type, assigned automatically by becsy. It | ||
* will stay the same across runs as long as the list of defs used to create the world doesn't | ||
* change. Feel free to use this for your own purposes but don't change it. | ||
*/ | ||
id?: number; | ||
__binding?: Binding<C>; | ||
__bind?(id: EntityId, writable: boolean): C; | ||
__create?(id: EntityId): C; | ||
__delete?(id: EntityId): void; | ||
} | ||
declare class Binding<C> { | ||
readonly type: ComponentType<C>; | ||
readonly fields: Field<any>[]; | ||
readonly dispatcher: Dispatcher; | ||
readonly maxEntities: number; | ||
capacity: number; | ||
readonly readonlyInstance: C; | ||
readonly writableInstance: C; | ||
readonly flagOffset: number; | ||
readonly flagMask: number; | ||
trackedWrites: boolean; | ||
entityId: number; | ||
index: number; | ||
constructor(type: ComponentType<C>, dispatcher: Dispatcher, maxEntities: number); | ||
constructor(type: ComponentType<C>, fields: Field<any>[], dispatcher: Dispatcher, capacity: number); | ||
} | ||
@@ -408,3 +414,3 @@ declare class World { | ||
execute(time?: number, delta?: number): void; | ||
get stats(): any; | ||
get stats(): Stats; | ||
} | ||
@@ -418,3 +424,4 @@ interface PropOptions<JSType> { | ||
declare function component(constructor: ComponentType<any>): void; | ||
declare function component(options: ComponentOptions): (constructor: ComponentType<any>) => void; | ||
export { World, Type, Entity, System, SystemType, componentTypes, component, prop, Query, ComponentType }; | ||
//# sourceMappingURL=perf.min.d.ts.map |
@@ -1,2 +0,2 @@ | ||
const e=new TextEncoder,t=new TextDecoder;function s(e){throw new Error(`Component is not writable; use entity.write(${e.type.name}) to acquire a writable version`)}class r{defaultValue;constructor(e){this.defaultValue=e}static boolean;static uint8;static int8;static uint16;static int16;static uint32;static int32;static float32;static float64;static staticString;static dynamicString;static ref}class i extends r{NumberArray;constructor(e){super(0),this.NumberArray=e}define(e,t,r){r||(r=new SharedArrayBuffer(e.maxEntities*this.NumberArray.BYTES_PER_ELEMENT));const i=new this.NumberArray(r);return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){i[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){s(e)}}),r}}class n extends r{choices;choicesIndex=new Map;TypedArray;constructor(e){if(super(e[0]),this.choices=e,!e?.length)throw new Error("No choices specified for Type.staticString");e.length<256?this.TypedArray=Uint8Array:e.length<65536?this.TypedArray=Uint16Array:this.TypedArray=Uint32Array;for(let t=0;t<e.length;t++)this.choicesIndex.set(e[t],t)}define(e,t,r){r||(r=new SharedArrayBuffer(e.maxEntities*this.TypedArray.BYTES_PER_ELEMENT));const i=new this.TypedArray(r),n=this.choices,a=this.choicesIndex;return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){const s=a.get(t);if(void 0===s)throw new Error(`Static string not in set: "${t}"`);i[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){s(e)}}),r}}class a extends r{maxUtf8Length;lengthsStride;bytesStride;constructor(e){super(""),this.maxUtf8Length=e+e%2,this.lengthsStride=e/2+1,this.bytesStride=this.maxUtf8Length+2}define(r,i,n){if(!n){const e=r.maxEntities*(this.maxUtf8Length+Uint16Array.BYTES_PER_ELEMENT);n=new SharedArrayBuffer(e)}const a=new Uint16Array(n),o=new Uint8Array(n),h=this.maxUtf8Length,d=this.lengthsStride,c=this.bytesStride;return Object.defineProperty(r.writableInstance,i,{enumerable:!0,configurable:!0,get(){const e=a[r.index*d];return t.decode(new Uint8Array(o.buffer,r.index*c+2,e))},set(t){const s=e.encode(t);if(s.byteLength>h)throw new Error(`Dynamic string length > ${h} after encoding: ${t}`);a[r.index*d]=s.byteLength,o.set(s,r.index*c+2)}}),Object.defineProperty(r.readonlyInstance,i,{enumerable:!0,configurable:!0,get(){const e=a[r.index*d];return t.decode(new Uint8Array(o.buffer,r.index*c+2,e))},set(e){s(r)}}),n}}r.boolean=new class extends r{constructor(){super(!1)}define(e,t,r){r||(r=new SharedArrayBuffer(e.maxEntities));const i=new Uint8Array(r);return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){i[e.index]=t?1:0}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){s(e)}}),r}},r.uint8=new i(Uint8Array),r.int8=new i(Int8Array),r.uint16=new i(Uint16Array),r.int16=new i(Int16Array),r.uint32=new i(Uint32Array),r.int32=new i(Int32Array),r.float32=new i(Float32Array),r.float64=new i(Float64Array),r.staticString=e=>new n(e),r.dynamicString=e=>new a(e),r.ref=new class extends r{constructor(){super(null)}define(e,t,r){r||(r=new SharedArrayBuffer(e.maxEntities*Int32Array.BYTES_PER_ELEMENT));const i=new Int32Array(r);return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){const s=i[e.index],r=t?.__id??-1;if(s===r)return;const n=e.dispatcher.indexer;0!==s&&n.remove(s,e.entityId),i[e.index]=r,0!==r&&n.insert(r,e.entityId)}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){s(e)}}),r}};class o{type;dispatcher;maxEntities;readonlyInstance;writableInstance;entityId=0;index=0;constructor(e,t,s){this.type=e,this.dispatcher=t,this.maxEntities=s,this.readonlyInstance=new e,this.writableInstance=new e}}function h(e,t,s){const i=Math.min(t.maxEntities??s.maxEntities,s.maxEntities);s.maxEntities,t.__id=e,t.__flagOffset=e>>5,t.__flagMask=1<<(31&e);const n=new o(t,s,i);t.__bind=(e,t)=>(n.entityId=e,n.index=e,t?n.writableInstance:n.readonlyInstance),t.__fields=function(e){const t=e.schema,s=[];for(const e in t){const i=t[e];let n;n=i instanceof r?{name:e,default:i.defaultValue,type:i}:Object.assign({name:e,default:i.type.defaultValue},i),s.push(n)}return s}(t);for(const e of t.__fields)e.type.define(n,e.name)}const d=Object.freeze({});class c{__registry;__id;joined;constructor(e){this.__registry=e}__reset(e){this.__id=e,this.joined=d}add(e,t){return this.__registry.setFlag(this.__id,e),function(e,t,s){const r=e.__bind(t,!0);for(const t of e.__fields)r[t.name]=s?.[t.name]??t.default}(e,this.__id,t),this}addAll(...e){for(let t=0;t<e.length;t++){const s=e[t];let r=e[t+1];"function"==typeof r?r=void 0:t++,this.add(s,r)}return this}remove(e){this.__remove(e)}removeAll(...e){for(const t of e)this.remove(t)}has(e,t=!1){return this.__registry.hasFlag(this.__id,e,t)}read(e){return e.__bind(this.__id,!1)}readRecentlyRemoved(e){return e.__bind(this.__id,!1)}write(e){return e.__trackedWrites&&this.__registry.trackWrite(this.__id,e),e.__bind(this.__id,!0)}delete(){for(const e of this.__registry.types)this.__registry.hasFlag(this.__id,e)&&this.__remove(e);this.__registry.queueDeletion(this.__id),this.__wipeInboundRefs()}__remove(e){this.__deindexOutboundRefs(e),this.__registry.clearFlag(this.__id,e)}__deindexOutboundRefs(e){const t=e.__fields;if(t.some((e=>e.type===r.ref))){const s=this.write(e);for(const e of t)e.type===r.ref&&(s[e.name]=null)}}__wipeInboundRefs(){}__checkMask(e,t){const s=this.__registry.executingSystem?.rwMasks,r=t?s?.write:s?.read;if(r&&!this.__registry.maskHasFlag(r,e))throw new Error(`System didn't mark component ${e.name} as ${t?"writable":"readable"}`)}}class l{maxRefs;buffer;index;numRefs=0;constructor(e){this.maxRefs=e,this.index=new Uint32Array(this.buffer=new SharedArrayBuffer(8*e))}insert(e,t){if(this.numRefs>=this.maxRefs)throw new Error(`Max number of refs reached: ${this.maxRefs}`);const s=this.findIndex(e,t);if(this.index[2*s]===e&&this.index[2*s+1]===t)throw new Error(`Internal error; ref already indexed: ${t} -> ${e}`);this.index.copyWithin(2*(s+1),2*s,2*(this.numRefs+1)),this.index[2*s]=e,this.index[2*s+1]=t,this.numRefs+=1}remove(e,t){const s=this.findIndex(e,t);if(this.index[2*s]===e&&this.index[2*s+1]===t)throw new Error(`Internal error; ref not found: ${t} -> ${e}`);this.numRefs-=1,this.index.copyWithin(s,s+1,this.numRefs-s)}*iterateReferrers(e){}findIndex(e,t){let s=0,r=this.numRefs-1,i=1;for(;s<r;){i=Math.floor((r-s+1)/2)+s;const n=this.index[2*i];n===e?void 0!==t&&this.index[2*i+1]<t?s=i+1:r=i-1:n<e?s=i+1:r=i-1}return s>=r&&(i=Math.max(0,Math.min(this.numRefs+1,s))),i}}class u{size;bytes;constructor(e){this.size=e,this.bytes=new Uint32Array(Math.ceil(e/32))}get(e){return 0!=(this.bytes[e>>>5]&1<<(31&e))}set(e){this.bytes[e>>>5]|=1<<(31&e)}unset(e){this.bytes[e>>>5]&=~(1<<(31&e))}clear(){this.bytes.fill(0)}}const g=[];class _{maxEntries;configParamName;data;corral;constructor(e,t){this.maxEntries=e,this.configParamName=t;const s=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.data=new Uint32Array(s);const r=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.corral=new Uint32Array(r)}push(e){const t=this.corral[0];t&&this.corral[t]===e||(this.corral[t+2]=e,this.corral[0]+=1)}commit(){const e=this.corral[0];if(!e)return;let t=this.data[0];const s=Math.min(e,this.maxEntries-t);for(this.data.set(this.corral.subarray(2,s+2),t+2),s<e&&this.data.set(this.corral.subarray(s+2,e+2),2),t+=e;t>=this.maxEntries;)t-=this.maxEntries,this.data[1]+=1;this.data[0]=t,this.corral[0]=0,this.corral[1]+=1}createPointer(e){return e?(e.index=this.data[0],e.generation=this.data[1],e.corralIndex=this.corral[0],e.corralGeneration=this.corral[1],e):{index:this.data[0],generation:this.data[1],corralIndex:this.corral[0],corralGeneration:this.corral[1]}}hasUpdatesSince(e){return!(e.index===this.data[0]&&e.generation===this.data[1]&&(e.corralGeneration===this.corral[1]?e.corralIndex===this.corral[0]:0===this.corral[0]))}processSince(e,t){let s=g;const r=t?.index??this.data[0],i=t?.generation??this.data[1];if(e.generation===i)if(e.index<r)s=[this.data,e.index+2,r+2],e.index=r;else{const t=this.corral[0],r=this.corral[1];(e.corralGeneration===r?e.corralIndex<t:t)&&(s=[this.corral,e.corralIndex+2,t+2],e.corralIndex=t,e.corralGeneration=r)}else s=[this.data,e.index+2,this.data.length],e.index=0,e.generation=i;return s}countSince(e,t){const s=e.index,r=t?.index??this.data[0],i=t?.generation??this.data[1];return e.index=r,e.generation=i,s===r&&e.generation===i?0:s<r?r-s:this.maxEntries-(s-r)}checkPointers(e,t){this.checkPointer(e),t&&this.checkPointer(t)}checkPointer(e){const t=this.data[0];let s=e.generation;e.index===t?s+1<this.data[1]&&this.throwCapacityExceeded():(e.index>t&&(s+=1),s!==this.data[1]&&this.throwCapacityExceeded())}throwCapacityExceeded(){throw new Error(`Log capacity exceeded, please raise ${this.configParamName} above ${this.maxEntries}`)}}class f{maxItems;configParamName;data;constructor(e,t){this.maxItems=e,this.configParamName=t,this.data=new Uint32Array(new SharedArrayBuffer((e+1)*Uint32Array.BYTES_PER_ELEMENT))}get length(){return this.data[0]}take(){const e=this.data[0]--;return this.data[e]}refill(e){if(!e.length)return;const t=this.length,s=t+e.length;this.data.set(e,t+1),this.data[0]=s}fillWithDescendingIntegers(e){const t=this.length;for(let s=this.data.length-1;s>t;s--)this.data[s]=e++;this.data[0]=this.data.length-1}}class m{pool;entities=[];constructor(e){this.pool=e}add(e){this.entities.push(this.pool.borrowTemporarily(e))}clear(){this.entities.length&&this.entities.splice(0,1/0)}}class y{pool;entities=[];lookupTable;constructor(e,t){this.pool=e,this.lookupTable=new Int32Array(t),this.lookupTable.fill(-1)}add(e){const t=this.entities.push(this.pool.borrow(e))-1;this.lookupTable[e]=t}remove(e){const t=this.lookupTable[e];if(t<0)throw new Error("Internal error, entity not in list");this.pool.return(e),this.lookupTable[e]=-1;const s=this.entities.pop();t<this.entities.length&&(this.entities[t]=s,this.lookupTable[s.__id]=t)}has(e){return this.lookupTable[e]>=0}clear(){throw new Error("Internal error, trying to clear persistent entity list")}}var p;!function(e){e[e.all=1]="all",e[e.added=2]="added",e[e.removed=4]="removed",e[e.changed=8]="changed",e[e.addedOrChanged=16]="addedOrChanged",e[e.changedOrRemoved=32]="changedOrRemoved",e[e.addedChangedOrRemoved=64]="addedChangedOrRemoved"}(p||(p={}));const w=p.added|p.removed|p.changed|p.addedOrChanged|p.changedOrRemoved|p.addedChangedOrRemoved,x=p.changed|p.addedOrChanged|p.changedOrRemoved|p.addedChangedOrRemoved;class b{query;system;results={};flavors=0;__withMask;__withoutMask;__trackMask;__refMask;hasTransientResults;hasChangedResults;currentEntities;changedEntities;constructor(e,t){this.query=e,this.system=t,e.__results=this.results,e.__systemName=t.name}complete(){const e=this.system.dispatcher;this.hasTransientResults=Boolean(this.flavors&w),this.hasChangedResults=Boolean(this.flavors&x),this.flavors&p.all?this.results.all=new y(e.registry.pool,e.maxEntities):this.currentEntities=new u(e.maxEntities),this.hasTransientResults&&this.allocateTransientResultLists(),this.flavors&&this.system.shapeQueries.push(this),this.hasChangedResults&&(this.changedEntities=new u(e.maxEntities),this.system.writeQueries.push(this))}allocateTransientResultLists(){this.flavors&p.added&&this.allocateResult("added"),this.flavors&p.removed&&this.allocateResult("removed"),this.flavors&p.changed&&this.allocateResult("changed"),this.flavors&p.addedOrChanged&&this.allocateResult("addedOrChanged"),this.flavors&p.changedOrRemoved&&this.allocateResult("changedOrRemoved"),this.flavors&p.addedChangedOrRemoved&&this.allocateResult("addedChangedOrRemoved")}allocateResult(e){const t=this.system.dispatcher;this.results[e]=new m(t.registry.pool)}clearTransientResults(){this.hasTransientResults&&(this.results.added?.clear(),this.results.removed?.clear(),this.results.changed?.clear(),this.results.addedOrChanged?.clear(),this.results.changedOrRemoved?.clear(),this.results.addedChangedOrRemoved?.clear(),this.changedEntities?.clear())}handleShapeUpdate(e){const t=this.system.dispatcher.registry,s=this.results.all?.has(e)??this.currentEntities.get(e),r=t.matchShape(e,this.__withMask,this.__withoutMask);r&&!s?(this.currentEntities?.set(e),this.results.all?.add(e),this.results.added?.add(e),this.results.addedOrChanged?.add(e),this.results.addedChangedOrRemoved?.add(e)):!r&&s&&(this.currentEntities?.unset(e),this.system.removedEntities.set(e),this.results.all?.remove(e),this.results.removed?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}handleWrite(e,t,s){!this.changedEntities.get(e)&&(this.__trackMask[t]??0)&s&&(this.changedEntities.set(e),this.results.changed?.add(e),this.results.addedOrChanged?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}}class E{__callback;__userQuery;__query;__system;__lastTypes;constructor(e,t){this.__callback=e,this.__userQuery=t}__build(e){try{this.__system=e,this.__query=new b(this.__userQuery,e),this.__callback(this),this.__query.complete()}catch(t){throw t.message=`Failed to build query in system ${e.name}: ${t.message}`,t}}get and(){return this}get but(){return this}get also(){return this}get all(){return this.__query.flavors|=p.all,this}get added(){return this.__query.flavors|=p.added,this}get removed(){return this.__query.flavors|=p.removed,this}get changed(){return this.__query.flavors|=p.changed,this}get addedOrChanged(){return this.__query.flavors|=p.addedOrChanged,this}get changedOrRemoved(){return this.__query.flavors|=p.changedOrRemoved,this}get addedChangedOrRemoved(){return this.__query.flavors|=p.addedChangedOrRemoved,this}with(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withMask"),this}without(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withoutMask",e),this}using(...e){return this.set(this.__system.rwMasks.read,e),this}get track(){this.set("__trackMask");for(const e of this.__lastTypes)e.__trackedWrites=!0;return this}get read(){return this}get write(){return this.set(this.__system.rwMasks.write),this}set(e,t,s){if(e){if(t||(t=this.__lastTypes),!t)throw new Error("No component type to apply query modifier to");if(this.__lastTypes=t,"string"==typeof e){if(s&&this.__query[e])throw new Error(`Only one ${s} allowed`);this.__query[e]||(this.__query[e]=[]),e=this.__query[e]}else if(s&&e.some((e=>0!==e)))throw new Error(`Only one ${s} allowed`);for(const s of t)this.__system.dispatcher.registry.extendMaskAndSetFlag(e,s)}}}class v{__results;__systemName;get all(){return this.__results.all.entities}get added(){return this.__results.added.entities}get removed(){return this.__results.removed.entities}get changed(){return this.__results.changed.entities}get addedOrChanged(){return this.__results.addedOrChanged.entities}get changedOrRemoved(){return this.__results.changedOrRemoved.entities}get addedChangedOrRemoved(){return this.__results.addedChangedOrRemoved.entities}__checkList(e){if(!this.__results[e])throw new Error(`Query '${e}' not configured, please add .${e} to your query definition in system ${this.__systemName}`)}}class R{__queryBuilders=[];__dispatcher;time;delta;get name(){return this.constructor.name}query(e){const t=new v,s=new E(e,t);return this.__queryBuilders.push(s),t}createEntity(...e){return this.__dispatcher.createEntity(e)}}class k{system;dispatcher;rwMasks={read:[],write:[]};shapeQueries=[];writeQueries=[];hasWriteQueries;processedEntities;removedEntities;shapeLogPointer;writeLogPointer;get name(){return this.system.name}constructor(e,t){this.system=e,this.dispatcher=t,e.__dispatcher=t,this.shapeLogPointer=t.shapeLog.createPointer(),this.writeLogPointer=t.writeLog?.createPointer(),this.processedEntities=new u(t.maxEntities),this.removedEntities=new u(t.maxEntities);for(const t of e.__queryBuilders)t.__build(this);e.__queryBuilders=null,this.hasWriteQueries=!!this.writeQueries.length}execute(e,t){this.system.time=e,this.system.delta=t,this.runQueries(),this.system.execute()}runQueries(){const e=this.dispatcher.shapeLog.hasUpdatesSince(this.shapeLogPointer),t=this.hasWriteQueries&&this.dispatcher.writeLog.hasUpdatesSince(this.writeLogPointer);if(e||t){this.processedEntities.clear();for(const e of this.shapeQueries)e.clearTransientResults();e&&this.__updateShapeQueries(),t&&this.__updateWriteQueries()}}__updateShapeQueries(){const e=this.dispatcher.shapeLog;let t,s,r;for(;[t,s,r]=e.processSince(this.shapeLogPointer),t;)for(let e=s;e<r;e++){const s=t[e];if(!this.processedEntities.get(s)){this.processedEntities.set(s);for(const e of this.shapeQueries)e.handleShapeUpdate(s)}}}__updateWriteQueries(){const e=this.dispatcher.writeLog;let t,s,r;for(;[t,s,r]=e.processSince(this.writeLogPointer),t;)for(let e=s;e<r;e++){const s=t[e],r=4194303&s;if(!this.processedEntities.get(r)){const e=s>>>22;for(const t of this.writeQueries)t.handleWrite(r,e>>5,1<<(31&e))}}}}class O{registry;borrowed;borrowCounts;spares=[];temporarilyBorrowedIds=[];constructor(e,t){this.registry=e,this.borrowed=Array.from({length:t}),this.borrowCounts=new Int32Array(t)}borrow(e){this.borrowCounts[e]+=1;let t=this.borrowed[e];return t||(t=this.borrowed[e]=this.spares.pop()??new c(this.registry),t.__reset(e)),t}borrowTemporarily(e){const t=this.borrow(e);return this.temporarilyBorrowedIds.push(e),t}returnTemporaryBorrows(){for(const e of this.temporarilyBorrowedIds)this.return(e);this.temporarilyBorrowedIds.splice(0,1/0)}return(e){--this.borrowCounts[e]<=0&&(this.spares.push(this.borrowed[e]),this.borrowed[e]=void 0)}}class S{types;dispatcher;stride;shapes;entityIdPool;pool;executingSystem;deletionLog;prevDeletionPointer;oldDeletionPointer;constructor(e,t,s,r){this.types=s,this.dispatcher=r;let i=0;for(const e of s)h(i++,e,this.dispatcher);this.stride=Math.ceil(s.length/32);const n=e*this.stride*4;this.shapes=new Uint32Array(new SharedArrayBuffer(n)),this.entityIdPool=new f(e,"maxEntities"),this.entityIdPool.fillWithDescendingIntegers(0),this.pool=new O(this,e),this.deletionLog=new _(t,"maxLimboEntities"),this.prevDeletionPointer=this.deletionLog.createPointer(),this.oldDeletionPointer=this.deletionLog.createPointer()}createEntity(e){const t=this.entityIdPool.take();this.shapes.fill(0,t*this.stride,(t+1)*this.stride);const s=this.pool.borrowTemporarily(t);return e&&s.addAll(...e),s}queueDeletion(e){this.deletionLog.push(e)}flush(){this.pool.returnTemporaryBorrows(),this.deletionLog.commit()}processEndOfFrame(){this.deletionLog.commit();let e,t,s,r=0;for(;[e,t,s]=this.deletionLog.processSince(this.oldDeletionPointer,this.prevDeletionPointer),e;){const i=e.subarray(t,s);this.entityIdPool.refill(i),r+=i.length}this.deletionLog.createPointer(this.prevDeletionPointer)}extendMaskAndSetFlag(e,t){const s=t.__flagOffset;s>=e.length&&(e.length=s+1,e.fill(0,e.length,s)),e[s]|=t.__flagMask}maskHasFlag(e,t){return 0!=((e?.[t.__flagOffset]??0)&t.__flagMask)}hasFlag(e,t,s=!1){const r=e*this.stride+t.__flagOffset;return 0!=(this.shapes[r]&t.__flagMask)||!(!s||!this.executingSystem?.removedEntities.get(e)||0==((this.executingSystem.rwMasks.read?.[t.__flagOffset]??0)&t.__flagMask))}setFlag(e,t){this.shapes[e*this.stride+t.__flagOffset]|=t.__flagMask,this.dispatcher.shapeLog.push(e)}clearFlag(e,t){this.shapes[e*this.stride+t.__flagOffset]&=~t.__flagMask,this.dispatcher.shapeLog.push(e)}trackWrite(e,t){this.dispatcher.writeLog.push(e|t.__id<<22)}matchShape(e,t,s){const r=e*this.stride;if(t)for(let e=0;e<t.length;e++){const s=t[e];if((this.shapes[r+e]&s)!==s)return!1}if(s)for(let e=0;e<s.length;e++){const t=s[e];if(0!=(this.shapes[r+e]&t))return!1}return!0}}const P="undefined"!=typeof window&&void 0!==window.performance?performance.now.bind(performance):Date.now.bind(Date);class I extends R{__callback;execute(){this.__callback(this)}}class L{maxEntities;indexer;registry;systems;lastTime=P()/1e3;executing;shapeLog;writeLog;shapeLogFramePointer;writeLogFramePointer;stats;userCallbackSystem;callbackSystem;constructor({defs:e,maxEntities:t=1e4,maxLimboEntities:s=Math.ceil(t/5),maxRefs:r=t,maxShapeChangesPerFrame:i=2*t,maxWritesPerFrame:n=4*t}){if(t>4194303)throw new Error("maxEntities too high, the limit is 4194303");const{componentTypes:a,systemTypes:o}=this.splitDefs(e);if(a.length>1024)throw new Error("Too many component types, the limit is 1024");this.maxEntities=t,this.shapeLog=new _(i,"maxShapeChangesPerFrame"),this.shapeLogFramePointer=this.shapeLog.createPointer(),this.indexer=new l(r),this.registry=new S(t,s,a.flat(1/0),this),this.systems=this.normalizeAndInitSystems(o),this.systems.some((e=>e.hasWriteQueries))&&(this.writeLog=new _(n,"maxWritesPerFrame"),this.writeLogFramePointer=this.writeLog.createPointer()),this.userCallbackSystem=new I,this.callbackSystem=new k(this.userCallbackSystem,this)}normalizeAndInitSystems(e){const t=[],s=e.flat(1/0);for(let e=0;e<s.length;e++){const r=new s[e],i=s[e+1];i&&"function"!=typeof i&&(Object.assign(r,i),e++),t.push(new k(r,this))}return t}splitDefs(e){const t=[],s=[];let r=!1;for(const i of e.flat(1/0))"function"==typeof i?(r=!i.schema,(r?s:t).push(i)):(s.push(i),r=!1);return{componentTypes:t,systemTypes:s}}execute(e,t,s){this.executing=!0,void 0===e&&(e=P()/1e3),void 0===t&&(t=e-this.lastTime),this.lastTime=e;for(const r of s??this.systems)this.registry.executingSystem=r,r.execute(e,t),this.flush();this.registry.executingSystem=void 0,this.registry.processEndOfFrame(),this.executing=!1}executeFunction(e){this.executing=!0,this.userCallbackSystem.__callback=e,this.callbackSystem.execute(0,0),this.flush(),this.registry.processEndOfFrame(),this.executing=!1}gatherFrameStats(){this.stats.frames+=1,this.stats.maxShapeChangesPerFrame=this.shapeLog.countSince(this.shapeLogFramePointer),this.stats.maxWritesPerFrame=this.writeLog?.countSince(this.writeLogFramePointer)??0}flush(){this.registry.flush(),this.shapeLog.commit(),this.writeLog?.commit()}createEntity(e){const t=this.registry.createEntity(e);return this.executing||this.flush(),t}}class A{__dispatcher;constructor(e){this.__dispatcher=new L(e)}build(e){this.__dispatcher.executeFunction(e)}createEntity(...e){this.__dispatcher.createEntity(e)}execute(e,t){this.__dispatcher.execute(e,t)}get stats(){return this.__dispatcher.stats}}function T(e){return function(t,s){t.constructor.schema||(t.constructor.schema={});const r="type"in e?e:{type:e};t.constructor.schema[s]=r}}const C=[];function M(e){C.push(e)}export{c as Entity,v as Query,R as System,r as Type,A as World,M as component,C as componentTypes,T as prop}; | ||
const e=new TextEncoder,t=new TextDecoder;function s(e){throw new Error(`Component is not writable; use entity.write(${e.type.name}) to acquire a writable version`)}class r{defaultValue;constructor(e){this.defaultValue=e}static boolean;static uint8;static int8;static uint16;static int16;static uint32;static int32;static float32;static float64;static staticString;static dynamicString;static ref;static object;static weakObject}class i extends r{NumberArray;constructor(e){super(0),this.NumberArray=e}defineElastic(e,t){let r,i;t.updateBuffer=()=>{const s=e.capacity*this.NumberArray.BYTES_PER_ELEMENT,n=t.buffer?.byteLength!==s;(n||t.buffer!==r)&&(r=n?new SharedArrayBuffer(s):t.buffer,i=new this.NumberArray(r),n&&t.buffer&&i.set(new this.NumberArray(t.buffer)),t.buffer=r)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){i[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){s(e)}})}defineFixed(e,t){const r=e.capacity*this.NumberArray.BYTES_PER_ELEMENT,i=new SharedArrayBuffer(r),n=new this.NumberArray(i);t.buffer=i,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>n[e.index],set(t){n[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>n[e.index],set(t){s(e)}})}}class n extends r{choices;choicesIndex=new Map;TypedArray;constructor(e){if(super(e[0]),this.choices=e,!e?.length)throw new Error("No choices specified for Type.staticString");e.length<256?this.TypedArray=Uint8Array:e.length<65536?this.TypedArray=Uint16Array:this.TypedArray=Uint32Array;for(let t=0;t<e.length;t++)this.choicesIndex.set(e[t],t)}defineElastic(e,t){let r,i;const n=this.choices,a=this.choicesIndex;t.updateBuffer=()=>{const s=e.capacity*this.TypedArray.BYTES_PER_ELEMENT,n=t.buffer?.byteLength!==s;(n||t.buffer!==r)&&(r=n?new SharedArrayBuffer(s):t.buffer,i=new this.TypedArray(r),n&&t.buffer&&i.set(new this.TypedArray(t.buffer)),t.buffer=r)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=i[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){const s=a.get(t);if(void 0===s)throw new Error(`Static string not in set: "${t}"`);i[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=i[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){s(e)}})}defineFixed(e,t){const r=this.choices,i=this.choicesIndex,n=e.capacity*this.TypedArray.BYTES_PER_ELEMENT,a=new SharedArrayBuffer(n),o=new this.TypedArray(a);t.buffer=a,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=o[e.index],s=r[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){const s=i.get(t);if(void 0===s)throw new Error(`Static string not in set: "${t}"`);o[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=o[e.index],s=r[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){s(e)}})}}class a extends r{maxUtf8Length;lengthsStride;bytesStride;constructor(e){super(""),this.maxUtf8Length=e+e%2,this.lengthsStride=e/2+1,this.bytesStride=this.maxUtf8Length+2}defineElastic(r,i){let n,a,o;const h=this.maxUtf8Length,d=this.lengthsStride,c=this.bytesStride;i.updateBuffer=()=>{const e=r.capacity*(this.maxUtf8Length+Uint16Array.BYTES_PER_ELEMENT),t=i.buffer?.byteLength!==e;(t||i.buffer!==n)&&(n=t?new SharedArrayBuffer(e):i.buffer,a=new Uint16Array(n),o=new Uint8Array(n),t&&i.buffer&&o.set(new Uint8Array(i.buffer)),i.buffer=n)},i.updateBuffer(),Object.defineProperty(r.writableInstance,i.name,{enumerable:!0,configurable:!0,get(){const e=a[r.index*d];return t.decode(new Uint8Array(o.buffer,r.index*c+2,e))},set(t){const s=e.encode(t);if(s.byteLength>h)throw new Error(`Dynamic string length > ${h} after encoding: ${t}`);a[r.index*d]=s.byteLength,o.set(s,r.index*c+2)}}),Object.defineProperty(r.readonlyInstance,i.name,{enumerable:!0,configurable:!0,get(){const e=a[r.index*d];return t.decode(new Uint8Array(o.buffer,r.index*c+2,e))},set(e){s(r)}})}defineFixed(r,i){const n=this.maxUtf8Length,a=this.lengthsStride,o=this.bytesStride,h=r.capacity*(this.maxUtf8Length+Uint16Array.BYTES_PER_ELEMENT),d=new SharedArrayBuffer(h),c=new Uint16Array(d),l=new Uint8Array(d);i.buffer=d,Object.defineProperty(r.writableInstance,i.name,{enumerable:!0,configurable:!0,get(){const e=c[r.index*a];return t.decode(new Uint8Array(l.buffer,r.index*o+2,e))},set(t){const s=e.encode(t);if(s.byteLength>n)throw new Error(`Dynamic string length > ${n} after encoding: ${t}`);c[r.index*a]=s.byteLength,l.set(s,r.index*o+2)}}),Object.defineProperty(r.readonlyInstance,i.name,{enumerable:!0,configurable:!0,get(){const e=c[r.index*a];return t.decode(new Uint8Array(l.buffer,r.index*o+2,e))},set(e){s(r)}})}}r.boolean=new class extends r{constructor(){super(!1)}defineElastic(e,t){let r,i;t.updateBuffer=()=>{const s=t.buffer?.byteLength!==e.capacity;(s||t.buffer!==r)&&(r=s?new SharedArrayBuffer(e.capacity):t.buffer,i=new Uint8Array(r),s&&t.buffer&&i.set(new Uint8Array(t.buffer)),t.buffer=r)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){i[e.index]=t?1:0}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){s(e)}})}defineFixed(e,t){const r=new SharedArrayBuffer(e.capacity),i=new Uint8Array(r);t.buffer=r,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){i[e.index]=t?1:0}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){s(e)}})}},r.uint8=new i(Uint8Array),r.int8=new i(Int8Array),r.uint16=new i(Uint16Array),r.int16=new i(Int16Array),r.uint32=new i(Uint32Array),r.int32=new i(Int32Array),r.float32=new i(Float32Array),r.float64=new i(Float64Array),r.staticString=e=>new n(e),r.dynamicString=e=>new a(e),r.ref=new class extends r{constructor(){super(null)}defineElastic(e,t){let r,i;t.updateBuffer=()=>{const s=e.capacity*Int32Array.BYTES_PER_ELEMENT,n=t.buffer?.byteLength!==s;(n||t.buffer!==r)&&(r=n?new SharedArrayBuffer(s):t.buffer,i=new Int32Array(r),n&&t.buffer&&i.set(new Int32Array(t.buffer)),t.buffer=r)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){const s=i[e.index],r=t?.__id??-1;s!==r&&(i[e.index]=r)}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){s(e)}})}defineFixed(e,t){const r=e.capacity*Int32Array.BYTES_PER_ELEMENT,i=new SharedArrayBuffer(r),n=new Int32Array(i);t.buffer=i,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=n[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){const s=n[e.index],r=t?.__id??-1;s!==r&&(n[e.index]=r)}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=n[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){s(e)}})}},r.object=new class extends r{constructor(){super(void 0)}defineElastic(e,t){const r=[];t.localBuffer=r,t.updateBuffer=()=>{},Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>r[e.index],set(t){r[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>r[e.index],set(t){s(e)}})}defineFixed(e,t){const r=new Array(e.capacity);t.localBuffer=r,t.updateBuffer=()=>{},Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>r[e.index],set(t){r[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>r[e.index],set(t){s(e)}})}},r.weakObject=new class extends r{finalizers;constructor(){super(void 0)}defineElastic(e,t){const r=[];t.localBuffer=r,t.updateBuffer=()=>{};const i=this.initFinalizers(e);Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=r[e.index];return null==t?t:t.deref()},set(s){if(null!=s){const r=new WeakRef(s);i?.register(s,{type:e.type,field:t,weakRef:r,id:e.entityId,index:e.index}),s=r}r[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=r[e.index];return null==t?t:t.deref()},set(t){s(e)}})}defineFixed(e,t){const r=new Array(e.capacity);t.localBuffer=r,t.updateBuffer=()=>{};const i=this.initFinalizers(e);Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=r[e.index];return null==t?t:t.deref()},set(s){if(null!=s){const r=new WeakRef(s);i?.register(s,{type:e.type,field:t,weakRef:r,id:e.entityId,index:e.index}),s=r}r[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=r[e.index];return null==t?t:t.deref()},set(t){s(e)}})}initFinalizers(e){if(!e.trackedWrites)return;if(this.finalizers)return this.finalizers;const t=e.dispatcher;return t.writeLog&&"undefined"!=typeof FinalizationRegistry?(this.finalizers=new FinalizationRegistry((({type:e,field:s,weakRef:r,id:i,index:n})=>{s.localBuffer?.[n]===r&&t.registry.trackWrite(i,e)})),this.finalizers):void 0}};class o{type;fields;dispatcher;capacity;readonlyInstance;writableInstance;flagOffset;flagMask;trackedWrites;entityId=0;index=0;constructor(e,t,s,r){this.type=e,this.fields=t,this.dispatcher=s,this.capacity=r,this.readonlyInstance=new e,this.writableInstance=new e,this.flagOffset=e.id>>5,this.flagMask=1<<(31&e.id)}}class h{maxEntities;binding;fields;elastic;index;spares;constructor(e,t,s,r){this.maxEntities=e,this.binding=t,this.fields=s,this.elastic=r,this.growSpares(),this.growCapacity()}acquireIndex(e){let t=this.index[e];return-1===t&&(this.spares[3]>0?t=this.spares[4+--this.spares[3]]:(this.spares[1]===this.spares[2]&&(this.binding.capacity=Math.min(this.maxEntities,2*this.binding.capacity),this.growCapacity()),t=this.spares[1]++),this.index[e]=t),t}releaseIndex(e){this.spares[3]===this.spares.length-4&&this.growSpares(),this.spares[4+this.spares[3]++]=this.index[e],this.index[e]=-1}growCapacity(){const e=this.ArrayType,t=e.BYTES_PER_ELEMENT!==this.spares?.[0];if(!this.index||t){const t=new e(new SharedArrayBuffer(this.maxEntities*e.BYTES_PER_ELEMENT));this.index?t.set(this.index):t.fill(-1),this.index=t}if(this.spares&&t){const t=new e(new SharedArrayBuffer(this.spares.length*e.BYTES_PER_ELEMENT));t.set(this.spares),t[0]=e.BYTES_PER_ELEMENT,this.spares=t}if(this.spares[2]=this.binding.capacity,this.elastic)for(const e of this.fields)e.updateBuffer()}growSpares(){const e=this.ArrayType,t=this.spares?Math.min(this.maxEntities,2*(this.spares.length-4)):8,s=new e(new SharedArrayBuffer((4+t)*e.BYTES_PER_ELEMENT));this.spares?s.set(this.spares):(s[0]=e.BYTES_PER_ELEMENT,s[2]=this.binding.capacity),this.spares=s}get ArrayType(){const e=this.binding.capacity;return e<=127?Int8Array:e<=32767?Int16Array:Int32Array}}function d(e,t,s){const i=t.options?.storage??s.defaultComponentStorage,n="sparse"===i?s.maxEntities:Math.min(s.maxEntities,t.options?.capacity??0),a=t.options?.initialCapacity??8;t.id=e;const d=new o(t,function(e){const t=e.schema,s=[];for(const e in t){const i=t[e];let n;n=i instanceof r?{name:e,default:i.defaultValue,type:i}:Object.assign({name:e,default:i.type.defaultValue},i),s.push(n)}return s}(t),s,n||a);t.__binding=d;for(const e of d.fields)n?e.type.defineFixed(d,e):e.type.defineElastic(d,e);switch(i){case"sparse":t.__bind=(e,t)=>(d.entityId=e,d.index=e,t?d.writableInstance:d.readonlyInstance),t.__create=e=>(d.entityId=e,d.index=e,d.writableInstance);break;case"packed":{const e=new h(s.maxEntities,d,d.fields,!n);t.__bind=(t,s)=>(d.entityId=t,d.index=e.index[t],s?d.writableInstance:d.readonlyInstance),t.__create=t=>(d.entityId=t,d.index=e.acquireIndex(t),d.writableInstance),t.__delete=t=>{e.releaseIndex(t)};break}case"compact":throw new Error("Not yet implemented")}}const c=Object.freeze({});class l{__registry;__id;joined;constructor(e){this.__registry=e}__reset(e){this.__id=e,this.joined=c}add(e,t){return this.__registry.setFlag(this.__id,e),function(e,t,s){const r=e.__create(t);for(const t of e.__binding.fields)r[t.name]=s?.[t.name]??t.default}(e,this.__id,t),this}addAll(...e){for(let t=0;t<e.length;t++){const s=e[t];let r=e[t+1];"function"==typeof r?r=void 0:t++,this.add(s,r)}return this}remove(e){this.__remove(e)}removeAll(...e){for(const t of e)this.remove(t)}has(e,t=!1){return this.__registry.hasFlag(this.__id,e,t)}read(e){return e.__bind(this.__id,!1)}readRecentlyRemoved(e){return e.__bind(this.__id,!1)}write(e){return e.__binding.trackedWrites&&this.__registry.trackWrite(this.__id,e),e.__bind(this.__id,!0)}delete(){for(const e of this.__registry.types)this.__registry.hasFlag(this.__id,e)&&this.__remove(e);this.__registry.queueDeletion(this.__id),this.__wipeInboundRefs()}__remove(e){this.__deindexOutboundRefs(e),e.__delete&&this.__registry.queueRemoval(this.__id,e),this.__registry.clearFlag(this.__id,e)}__deindexOutboundRefs(e){const t=e.__binding.fields;if(t.some((e=>e.type===r.ref))){const s=this.write(e);for(const e of t)e.type===r.ref&&(s[e.name]=null)}}__wipeInboundRefs(){}__checkMask(e,t){const s=this.__registry.executingSystem?.rwMasks,r=t?s?.write:s?.read;if(r&&!this.__registry.maskHasFlag(r,e))throw new Error(`System didn't mark component ${e.name} as ${t?"writable":"readable"}`)}}class u{size;bytes;constructor(e){this.size=e,this.bytes=new Uint32Array(Math.ceil(e/32))}get(e){return 0!=(this.bytes[e>>>5]&1<<(31&e))}set(e){this.bytes[e>>>5]|=1<<(31&e)}unset(e){this.bytes[e>>>5]&=~(1<<(31&e))}clear(){this.bytes.fill(0)}}const f=[];class g{maxEntries;configParamName;data;corral;constructor(e,t){this.maxEntries=e,this.configParamName=t;const s=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.data=new Uint32Array(s);const r=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.corral=new Uint32Array(r)}push(e){const t=this.corral[0];t&&this.corral[t]===e||(this.corral[t+2]=e,this.corral[0]+=1)}commit(){const e=this.corral[0];if(!e)return;let t=this.data[0];const s=Math.min(e,this.maxEntries-t);for(this.data.set(this.corral.subarray(2,s+2),t+2),s<e&&this.data.set(this.corral.subarray(s+2,e+2),2),t+=e;t>=this.maxEntries;)t-=this.maxEntries,this.data[1]+=1;this.data[0]=t,this.corral[0]=0,this.corral[1]+=1}createPointer(e){return e?(e.index=this.data[0],e.generation=this.data[1],e.corralIndex=this.corral[0],e.corralGeneration=this.corral[1],e):{index:this.data[0],generation:this.data[1],corralIndex:this.corral[0],corralGeneration:this.corral[1]}}hasUpdatesSince(e){return!(e.index===this.data[0]&&e.generation===this.data[1]&&(e.corralGeneration===this.corral[1]?e.corralIndex===this.corral[0]:0===this.corral[0]))}processSince(e,t){let s=f;const r=t?.index??this.data[0],i=t?.generation??this.data[1];if(e.generation===i)if(e.index<r)s=[this.data,e.index+2,r+2],e.index=r;else{const t=this.corral[0],r=this.corral[1];(e.corralGeneration===r?e.corralIndex<t:t)&&(s=[this.corral,e.corralIndex+2,t+2],e.corralIndex=t,e.corralGeneration=r)}else s=[this.data,e.index+2,this.data.length],e.index=0,e.generation=i;return s}countSince(e,t){const s=e.index,r=t?.index??this.data[0],i=t?.generation??this.data[1];return e.index=r,e.generation=i,s===r&&e.generation===i?0:s<r?r-s:this.maxEntries-(s-r)}checkPointers(e,t){this.checkPointer(e),t&&this.checkPointer(t)}checkPointer(e){const t=this.data[0];let s=e.generation;e.index===t?s+1<this.data[1]&&this.throwCapacityExceeded():(e.index>t&&(s+=1),s!==this.data[1]&&this.throwCapacityExceeded())}throwCapacityExceeded(){throw new Error(`Log capacity exceeded, please raise ${this.configParamName} above ${this.maxEntries}`)}}class y{maxItems;configParamName;data;constructor(e,t){this.maxItems=e,this.configParamName=t,this.data=new Uint32Array(new SharedArrayBuffer((e+1)*Uint32Array.BYTES_PER_ELEMENT))}get length(){return this.data[0]}take(){const e=this.data[0]--;return this.data[e]}refill(e){if(!e.length)return;const t=this.length,s=t+e.length;this.data.set(e,t+1),this.data[0]=s}fillWithDescendingIntegers(e){const t=this.length;for(let s=this.data.length-1;s>t;s--)this.data[s]=e++;this.data[0]=this.data.length-1}}class p{pool;entities=[];constructor(e){this.pool=e}add(e){this.entities.push(this.pool.borrowTemporarily(e))}clear(){this.entities.length&&this.entities.splice(0,1/0)}}class m{pool;entities=[];lookupTable;constructor(e,t){this.pool=e,this.lookupTable=new Int32Array(t),this.lookupTable.fill(-1)}add(e){const t=this.entities.push(this.pool.borrow(e))-1;this.lookupTable[e]=t}remove(e){const t=this.lookupTable[e];if(t<0)throw new Error("Internal error, entity not in list");this.pool.return(e),this.lookupTable[e]=-1;const s=this.entities.pop();t<this.entities.length&&(this.entities[t]=s,this.lookupTable[s.__id]=t)}has(e){return this.lookupTable[e]>=0}clear(){throw new Error("Internal error, trying to clear persistent entity list")}}var _;!function(e){e[e.all=1]="all",e[e.added=2]="added",e[e.removed=4]="removed",e[e.changed=8]="changed",e[e.addedOrChanged=16]="addedOrChanged",e[e.changedOrRemoved=32]="changedOrRemoved",e[e.addedChangedOrRemoved=64]="addedChangedOrRemoved"}(_||(_={}));const b=_.added|_.removed|_.changed|_.addedOrChanged|_.changedOrRemoved|_.addedChangedOrRemoved,w=_.changed|_.addedOrChanged|_.changedOrRemoved|_.addedChangedOrRemoved;class x{query;system;results={};flavors=0;__withMask;__withoutMask;__trackMask;__refMask;hasTransientResults;hasChangedResults;currentEntities;changedEntities;constructor(e,t){this.query=e,this.system=t,e.__results=this.results,e.__systemName=t.name}complete(){const e=this.system.dispatcher;this.hasTransientResults=Boolean(this.flavors&b),this.hasChangedResults=Boolean(this.flavors&w),this.flavors&_.all?this.results.all=new m(e.registry.pool,e.maxEntities):this.currentEntities=new u(e.maxEntities),this.hasTransientResults&&this.allocateTransientResultLists(),this.flavors&&this.system.shapeQueries.push(this),this.hasChangedResults&&(this.changedEntities=new u(e.maxEntities),this.system.writeQueries.push(this))}allocateTransientResultLists(){this.flavors&_.added&&this.allocateResult("added"),this.flavors&_.removed&&this.allocateResult("removed"),this.flavors&_.changed&&this.allocateResult("changed"),this.flavors&_.addedOrChanged&&this.allocateResult("addedOrChanged"),this.flavors&_.changedOrRemoved&&this.allocateResult("changedOrRemoved"),this.flavors&_.addedChangedOrRemoved&&this.allocateResult("addedChangedOrRemoved")}allocateResult(e){const t=this.system.dispatcher;this.results[e]=new p(t.registry.pool)}clearTransientResults(){this.hasTransientResults&&(this.results.added?.clear(),this.results.removed?.clear(),this.results.changed?.clear(),this.results.addedOrChanged?.clear(),this.results.changedOrRemoved?.clear(),this.results.addedChangedOrRemoved?.clear(),this.changedEntities?.clear())}handleShapeUpdate(e){const t=this.system.dispatcher.registry,s=this.results.all?.has(e)??this.currentEntities.get(e),r=t.matchShape(e,this.__withMask,this.__withoutMask);r&&!s?(this.currentEntities?.set(e),this.results.all?.add(e),this.results.added?.add(e),this.results.addedOrChanged?.add(e),this.results.addedChangedOrRemoved?.add(e)):!r&&s&&(this.currentEntities?.unset(e),this.results.all?.remove(e),this.results.removed?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}handleWrite(e,t,s){!this.changedEntities.get(e)&&(this.__trackMask[t]??0)&s&&(this.changedEntities.set(e),this.results.changed?.add(e),this.results.addedOrChanged?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}}class E{__callback;__userQuery;__query;__system;__lastTypes;constructor(e,t){this.__callback=e,this.__userQuery=t}__build(e){try{this.__system=e,this.__query=new x(this.__userQuery,e),this.__callback(this),this.__query.complete()}catch(t){throw t.message=`Failed to build query in system ${e.name}: ${t.message}`,t}}get and(){return this}get but(){return this}get also(){return this}get all(){return this.__query.flavors|=_.all,this}get added(){return this.__query.flavors|=_.added,this}get removed(){return this.__query.flavors|=_.removed,this}get changed(){return this.__query.flavors|=_.changed,this}get addedOrChanged(){return this.__query.flavors|=_.addedOrChanged,this}get changedOrRemoved(){return this.__query.flavors|=_.changedOrRemoved,this}get addedChangedOrRemoved(){return this.__query.flavors|=_.addedChangedOrRemoved,this}with(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withMask"),this}without(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withoutMask",e),this}using(...e){return this.set(this.__system.rwMasks.read,e),this}get track(){this.set("__trackMask");for(const e of this.__lastTypes)e.__binding.trackedWrites=!0;return this}get read(){return this}get write(){return this.set(this.__system.rwMasks.write),this}set(e,t,s){if(e){if(t||(t=this.__lastTypes),!t)throw new Error("No component type to apply query modifier to");if(this.__lastTypes=t,"string"==typeof e){if(s&&this.__query[e])throw new Error(`Only one ${s} allowed`);this.__query[e]||(this.__query[e]=[]),e=this.__query[e]}else if(s&&e.some((e=>0!==e)))throw new Error(`Only one ${s} allowed`);for(const s of t)this.__system.dispatcher.registry.extendMaskAndSetFlag(e,s)}}}class v{__results;__systemName;get all(){return this.__results.all.entities}get added(){return this.__results.added.entities}get removed(){return this.__results.removed.entities}get changed(){return this.__results.changed.entities}get addedOrChanged(){return this.__results.addedOrChanged.entities}get changedOrRemoved(){return this.__results.changedOrRemoved.entities}get addedChangedOrRemoved(){return this.__results.addedChangedOrRemoved.entities}__checkList(e){if(!this.__results[e])throw new Error(`Query '${e}' not configured, please add .${e} to your query definition in system ${this.__systemName}`)}}class P{__queryBuilders=[];__dispatcher;time;delta;get name(){return this.constructor.name}query(e){const t=new v,s=new E(e,t);return this.__queryBuilders.push(s),t}createEntity(...e){return this.__dispatcher.createEntity(e)}}class S{system;dispatcher;rwMasks={read:[],write:[]};shapeQueries=[];writeQueries=[];hasWriteQueries;processedEntities;shapeLogPointer;writeLogPointer;get name(){return this.system.name}constructor(e,t){this.system=e,this.dispatcher=t,e.__dispatcher=t,this.shapeLogPointer=t.shapeLog.createPointer(),this.writeLogPointer=t.writeLog?.createPointer(),this.processedEntities=new u(t.maxEntities);for(const t of e.__queryBuilders)t.__build(this);e.__queryBuilders=null,this.hasWriteQueries=!!this.writeQueries.length}execute(e,t){this.system.time=e,this.system.delta=t,this.runQueries(),this.system.execute()}runQueries(){const e=this.dispatcher.shapeLog.hasUpdatesSince(this.shapeLogPointer),t=this.hasWriteQueries&&this.dispatcher.writeLog.hasUpdatesSince(this.writeLogPointer);if(e||t){this.processedEntities.clear();for(const e of this.shapeQueries)e.clearTransientResults();e&&this.__updateShapeQueries(),t&&this.__updateWriteQueries()}}__updateShapeQueries(){const e=this.dispatcher.shapeLog;let t,s,r;for(;[t,s,r]=e.processSince(this.shapeLogPointer),t;)for(let e=s;e<r;e++){const s=t[e];if(!this.processedEntities.get(s)){this.processedEntities.set(s);for(const e of this.shapeQueries)e.handleShapeUpdate(s)}}}__updateWriteQueries(){const e=this.dispatcher.writeLog;let t,s,r;for(;[t,s,r]=e.processSince(this.writeLogPointer),t;)for(let e=s;e<r;e++){const s=t[e],r=4194303&s;if(!this.processedEntities.get(r)){const e=s>>>22;for(const t of this.writeQueries)t.handleWrite(r,e>>5,1<<(31&e))}}}}class L{registry;borrowed;borrowCounts;spares=[];temporarilyBorrowedIds=[];constructor(e,t){this.registry=e,this.borrowed=Array.from({length:t}),this.borrowCounts=new Int32Array(t)}borrow(e){this.borrowCounts[e]+=1;let t=this.borrowed[e];return t||(t=this.borrowed[e]=this.spares.pop()??new l(this.registry),t.__reset(e)),t}borrowTemporarily(e){const t=this.borrow(e);return this.temporarilyBorrowedIds.push(e),t}returnTemporaryBorrows(){for(const e of this.temporarilyBorrowedIds)this.return(e);this.temporarilyBorrowedIds.splice(0,1/0)}return(e){--this.borrowCounts[e]<=0&&(this.spares.push(this.borrowed[e]),this.borrowed[e]=void 0)}}class I{types;dispatcher;stride;shapes;staleShapes;entityIdPool;pool;executingSystem;deletionLog;prevDeletionPointer;oldDeletionPointer;removalLog;prevRemovalPointer;oldRemovalPointer;constructor(e,t,s,r,i){this.types=r,this.dispatcher=i;let n=0;for(const e of r)d(n++,e,this.dispatcher);this.stride=Math.ceil(r.length/32);const a=e*this.stride*4;this.shapes=new Uint32Array(new SharedArrayBuffer(a)),this.staleShapes=new Uint32Array(new SharedArrayBuffer(a)),this.entityIdPool=new y(e,"maxEntities"),this.entityIdPool.fillWithDescendingIntegers(0),this.pool=new L(this,e),this.deletionLog=new g(t,"maxLimboEntities"),this.prevDeletionPointer=this.deletionLog.createPointer(),this.oldDeletionPointer=this.deletionLog.createPointer(),this.removalLog=new g(s,"maxLimboComponents"),this.prevRemovalPointer=this.removalLog.createPointer(),this.oldRemovalPointer=this.removalLog.createPointer()}createEntity(e){const t=this.entityIdPool.take();this.shapes.fill(0,t*this.stride,(t+1)*this.stride);const s=this.pool.borrowTemporarily(t);return e&&s.addAll(...e),s}queueDeletion(e){this.deletionLog.push(e)}queueRemoval(e,t){this.removalLog.push(e|t.id<<22)}flush(){this.pool.returnTemporaryBorrows(),this.deletionLog.commit(),this.removalLog.commit()}processEndOfFrame(){this.processDeletionLog(),this.processRemovalLog()}processDeletionLog(){this.deletionLog.commit();let e,t,s,r=0;for(;[e,t,s]=this.deletionLog.processSince(this.oldDeletionPointer,this.prevDeletionPointer),e;){const i=e.subarray(t,s);this.entityIdPool.refill(i),r+=i.length}this.deletionLog.createPointer(this.prevDeletionPointer)}processRemovalLog(){let e,t,s;for(this.removalLog.commit();[e,t,s]=this.removalLog.processSince(this.oldRemovalPointer,this.prevRemovalPointer),e;)for(let r=t;r<s;r++){const t=e[r],s=4194303&t,i=t>>>22,n=this.types[i],a=s*this.stride+n.__binding.flagOffset,o=n.__binding.flagMask;0==(this.shapes[a]&o)&&(this.staleShapes[a]&=~o,n.__delete(s))}this.removalLog.createPointer(this.prevRemovalPointer)}extendMaskAndSetFlag(e,t){const s=t.__binding.flagOffset;s>=e.length&&(e.length=s+1,e.fill(0,e.length,s)),e[s]|=t.__binding.flagMask}maskHasFlag(e,t){return 0!=((e?.[t.__binding.flagOffset]??0)&t.__binding.flagMask)}hasFlag(e,t,s=!1){const r=e*this.stride+t.__binding.flagOffset,i=t.__binding.flagMask;return 0!=(this.shapes[r]&i)||!(!s||0==(this.staleShapes[r]&i))}setFlag(e,t){const s=e*this.stride+t.__binding.flagOffset,r=t.__binding.flagMask;this.shapes[s]|=r,this.staleShapes[s]|=r,this.dispatcher.shapeLog.push(e)}clearFlag(e,t){this.shapes[e*this.stride+t.__binding.flagOffset]&=~t.__binding.flagMask,this.dispatcher.shapeLog.push(e)}trackWrite(e,t){this.dispatcher.writeLog.push(e|t.id<<22)}matchShape(e,t,s){const r=e*this.stride;if(t)for(let e=0;e<t.length;e++){const s=t[e];if((this.shapes[r+e]&s)!==s)return!1}if(s)for(let e=0;e<s.length;e++){const t=s[e];if(0!=(this.shapes[r+e]&t))return!1}return!0}}const A="undefined"!=typeof window&&void 0!==window.performance?performance.now.bind(performance):Date.now.bind(Date);class O extends P{__callback;execute(){this.__callback(this)}}class R{maxEntities;defaultComponentStorage;registry;systems;lastTime=A()/1e3;executing;shapeLog;writeLog;shapeLogFramePointer;writeLogFramePointer;stats;userCallbackSystem;callbackSystem;constructor({defs:e,maxEntities:t=1e4,maxLimboEntities:s=Math.ceil(t/5),maxLimboComponents:r=Math.ceil(t/5),maxRefs:i=t,maxShapeChangesPerFrame:n=2*t,maxWritesPerFrame:a=4*t,defaultComponentStorage:o="sparse"}){if(t>4194303)throw new Error("maxEntities too high, the limit is 4194303");const{componentTypes:h,systemTypes:d}=this.splitDefs(e);if(h.length>1024)throw new Error("Too many component types, the limit is 1024");this.maxEntities=t,this.defaultComponentStorage=o,this.shapeLog=new g(n,"maxShapeChangesPerFrame"),this.shapeLogFramePointer=this.shapeLog.createPointer(),this.registry=new I(t,s,r,h.flat(1/0),this),this.systems=this.normalizeAndInitSystems(d),this.systems.some((e=>e.hasWriteQueries))&&(this.writeLog=new g(a,"maxWritesPerFrame"),this.writeLogFramePointer=this.writeLog.createPointer()),this.userCallbackSystem=new O,this.callbackSystem=new S(this.userCallbackSystem,this)}normalizeAndInitSystems(e){const t=[],s=e.flat(1/0);for(let e=0;e<s.length;e++){const r=new s[e],i=s[e+1];i&&"function"!=typeof i&&(Object.assign(r,i),e++),t.push(new S(r,this))}return t}splitDefs(e){const t=[],s=[];let r=!1;for(const i of e.flat(1/0))"function"==typeof i?(r=!i.schema,(r?s:t).push(i)):(s.push(i),r=!1);return{componentTypes:t,systemTypes:s}}execute(e,t,s){this.executing=!0,void 0===e&&(e=A()/1e3),void 0===t&&(t=e-this.lastTime),this.lastTime=e;for(const r of s??this.systems)this.registry.executingSystem=r,r.execute(e,t),this.flush();this.registry.executingSystem=void 0,this.registry.processEndOfFrame(),this.executing=!1}executeFunction(e){this.executing=!0,this.userCallbackSystem.__callback=e,this.callbackSystem.execute(0,0),this.flush(),this.registry.processEndOfFrame(),this.executing=!1}gatherFrameStats(){this.stats.frames+=1,this.stats.maxShapeChangesPerFrame=this.shapeLog.countSince(this.shapeLogFramePointer),this.stats.maxWritesPerFrame=this.writeLog?.countSince(this.writeLogFramePointer)??0}flush(){this.registry.flush(),this.shapeLog.commit(),this.writeLog?.commit()}createEntity(e){const t=this.registry.createEntity(e);return this.executing||this.flush(),t}}class T{__dispatcher;constructor(e){this.__dispatcher=new R(e)}build(e){this.__dispatcher.executeFunction(e)}createEntity(...e){this.__dispatcher.createEntity(e)}execute(e,t){this.__dispatcher.execute(e,t)}get stats(){return this.__dispatcher.stats}}function k(e){return function(t,s){t.constructor.schema||(t.constructor.schema={});const r="type"in e?e:{type:e};t.constructor.schema[s]=r}}const B=[];function C(e){if("function"!=typeof e)return t=>{t.options=e,B.push(t)};B.push(e)}export{l as Entity,v as Query,P as System,r as Type,T as World,C as component,B as componentTypes,k as prop}; | ||
//# sourceMappingURL=perf.min.js.map |
@@ -1,38 +0,1 @@ | ||
declare class Indexer { | ||
private readonly maxRefs; | ||
private readonly buffer; | ||
private readonly index; | ||
private numRefs; | ||
constructor(maxRefs: number); | ||
/** | ||
* Inserts a new reference entry into the index, but fails if this exact pair is already indexed. | ||
*/ | ||
insert(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Removes a reference entry from the index, failing if it's missing. | ||
*/ | ||
remove(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Returns an iterable over the IDs of all entities that refer to the given one. | ||
*/ | ||
iterateReferrers(referenceId: EntityId): Iterable<EntityId>; | ||
/** | ||
* Finds the entry that matches referencedId:referrerId exactly. If not found, finds the first | ||
* entry that would follow referenceId:referrerId. If referrerId is not specified, finds the | ||
* first referenceId:* entry, if any. | ||
*/ | ||
private findIndex; | ||
} | ||
/** | ||
* A fixed but arbitrary size bitset. | ||
*/ | ||
declare class Bitset { | ||
private readonly size; | ||
private readonly bytes; | ||
constructor(size: number); | ||
get(index: number): boolean; | ||
set(index: number): void; | ||
unset(index: number): void; | ||
clear(): void; | ||
} | ||
interface LogPointer { | ||
@@ -215,3 +178,2 @@ index: number; | ||
private processedEntities; | ||
removedEntities: Bitset; | ||
private shapeLogPointer; | ||
@@ -226,10 +188,9 @@ private writeLogPointer; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defs: DefsArray; | ||
declare class ComponentStats { | ||
_numEntities: number; | ||
maxEntities: number; | ||
capacity: number; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
toString(): string; | ||
} | ||
@@ -240,22 +201,40 @@ declare class Stats { | ||
maxEntities: number; | ||
_maxLimboEntities: number; | ||
_maxLimboComponents: number; | ||
_numRefs: number; | ||
maxRefs: number; | ||
_maxShapeChangesPerFrame: number; | ||
_maxWritesPerFrame: number; | ||
components: { | ||
[typeName: string]: ComponentStats; | ||
}; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
_maxLimboEntities: number; | ||
get maxLimboEntities(): number; | ||
set maxLimboEntities(value: number); | ||
_numRefs: number; | ||
maxRefs: number; | ||
get maxLimboComponents(): number; | ||
set maxLimboComponents(value: number); | ||
get numRefs(): number; | ||
set numRefs(value: number); | ||
_maxShapeChangesPerFrame: number; | ||
get maxShapeChangesPerFrame(): number; | ||
set maxShapeChangesPerFrame(value: number); | ||
_maxWritesPerFrame: number; | ||
get maxWritesPerFrame(): number; | ||
set maxWritesPerFrame(value: number); | ||
for(type: ComponentType<any>): ComponentStats; | ||
toString(): string; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
defs: DefsArray; | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxLimboComponents?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defaultComponentStorage?: ComponentStorage; | ||
} | ||
declare class Dispatcher { | ||
readonly maxEntities: number; | ||
readonly indexer: Indexer; | ||
readonly defaultComponentStorage: ComponentStorage; | ||
readonly registry: Registry; | ||
@@ -272,3 +251,3 @@ readonly systems: SystemBox[]; | ||
private readonly callbackSystem; | ||
constructor({ defs, maxEntities, maxLimboEntities, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame }: WorldOptions); | ||
constructor({ defs, maxEntities, maxLimboEntities, maxLimboComponents, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame, defaultComponentStorage }: WorldOptions); | ||
private normalizeAndInitSystems; | ||
@@ -299,2 +278,3 @@ private splitDefs; | ||
private readonly shapes; | ||
private readonly staleShapes; | ||
private readonly entityIdPool; | ||
@@ -306,7 +286,13 @@ readonly pool: EntityPool; | ||
private readonly oldDeletionPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
private readonly removalLog; | ||
private readonly prevRemovalPointer; | ||
private readonly oldRemovalPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, maxLimboComponents: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
createEntity(initialComponents: (ComponentType<any> | any)[]): Entity; | ||
queueDeletion(id: EntityId): void; | ||
queueRemoval(id: EntityId, type: ComponentType<any>): void; | ||
flush(): void; | ||
processEndOfFrame(): void; | ||
private processDeletionLog; | ||
private processRemovalLog; | ||
extendMaskAndSetFlag(mask: number[], type: ComponentType<any>): void; | ||
@@ -350,3 +336,4 @@ maskHasFlag(mask: number[] | undefined, type: ComponentType<any>): boolean; | ||
constructor(defaultValue: JSType); | ||
abstract define<C>(binding: Binding<C>, name: string, buffer?: SharedArrayBuffer): SharedArrayBuffer; | ||
abstract defineElastic(binding: Binding<any>, field: Field<any>): void; | ||
abstract defineFixed(binding: Binding<any>, field: Field<any>): void; | ||
static boolean: Type<boolean>; | ||
@@ -364,2 +351,4 @@ static uint8: Type<number>; | ||
static ref: Type<Entity | null>; | ||
static object: Type<any>; | ||
static weakObject: Type<any>; | ||
} | ||
@@ -373,2 +362,8 @@ interface SchemaDef<JSType> { | ||
} | ||
type ComponentStorage = "sparse" | "packed" | "compact"; | ||
interface ComponentOptions { | ||
storage?: ComponentStorage; | ||
capacity?: number; | ||
initialCapacity?: number; | ||
} | ||
interface Field<JSType> { | ||
@@ -378,2 +373,5 @@ name: string; | ||
default: JSType; | ||
buffer?: SharedArrayBuffer; | ||
localBuffer?: any[]; | ||
updateBuffer?(): void; | ||
} | ||
@@ -383,19 +381,27 @@ interface ComponentType<C> { | ||
schema?: Schema; | ||
maxEntities?: number; | ||
__id?: number; | ||
__flagOffset?: number; | ||
__flagMask?: number; | ||
__trackedWrites?: boolean; | ||
__fields?: Field<any>[]; | ||
options?: ComponentOptions; | ||
/** | ||
* A unique, sequential id number for this component type, assigned automatically by becsy. It | ||
* will stay the same across runs as long as the list of defs used to create the world doesn't | ||
* change. Feel free to use this for your own purposes but don't change it. | ||
*/ | ||
id?: number; | ||
__binding?: Binding<C>; | ||
__bind?(id: EntityId, writable: boolean): C; | ||
__create?(id: EntityId): C; | ||
__delete?(id: EntityId): void; | ||
} | ||
declare class Binding<C> { | ||
readonly type: ComponentType<C>; | ||
readonly fields: Field<any>[]; | ||
readonly dispatcher: Dispatcher; | ||
readonly maxEntities: number; | ||
capacity: number; | ||
readonly readonlyInstance: C; | ||
readonly writableInstance: C; | ||
readonly flagOffset: number; | ||
readonly flagMask: number; | ||
trackedWrites: boolean; | ||
entityId: number; | ||
index: number; | ||
constructor(type: ComponentType<C>, dispatcher: Dispatcher, maxEntities: number); | ||
constructor(type: ComponentType<C>, fields: Field<any>[], dispatcher: Dispatcher, capacity: number); | ||
} | ||
@@ -408,3 +414,3 @@ declare class World { | ||
execute(time?: number, delta?: number): void; | ||
get stats(): any; | ||
get stats(): Stats; | ||
} | ||
@@ -418,3 +424,4 @@ interface PropOptions<JSType> { | ||
declare function component(constructor: ComponentType<any>): void; | ||
declare function component(options: ComponentOptions): (constructor: ComponentType<any>) => void; | ||
export { World, Type, Entity, System, SystemType, componentTypes, component, prop, Query, ComponentType }; | ||
//# sourceMappingURL=perf.umd.d.ts.map |
@@ -1,38 +0,1 @@ | ||
declare class Indexer { | ||
private readonly maxRefs; | ||
private readonly buffer; | ||
private readonly index; | ||
private numRefs; | ||
constructor(maxRefs: number); | ||
/** | ||
* Inserts a new reference entry into the index, but fails if this exact pair is already indexed. | ||
*/ | ||
insert(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Removes a reference entry from the index, failing if it's missing. | ||
*/ | ||
remove(referenceId: EntityId, referrerId: EntityId): void; | ||
/** | ||
* Returns an iterable over the IDs of all entities that refer to the given one. | ||
*/ | ||
iterateReferrers(referenceId: EntityId): Iterable<EntityId>; | ||
/** | ||
* Finds the entry that matches referencedId:referrerId exactly. If not found, finds the first | ||
* entry that would follow referenceId:referrerId. If referrerId is not specified, finds the | ||
* first referenceId:* entry, if any. | ||
*/ | ||
private findIndex; | ||
} | ||
/** | ||
* A fixed but arbitrary size bitset. | ||
*/ | ||
declare class Bitset { | ||
private readonly size; | ||
private readonly bytes; | ||
constructor(size: number); | ||
get(index: number): boolean; | ||
set(index: number): void; | ||
unset(index: number): void; | ||
clear(): void; | ||
} | ||
interface LogPointer { | ||
@@ -215,3 +178,2 @@ index: number; | ||
private processedEntities; | ||
removedEntities: Bitset; | ||
private shapeLogPointer; | ||
@@ -226,10 +188,9 @@ private writeLogPointer; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defs: DefsArray; | ||
declare class ComponentStats { | ||
_numEntities: number; | ||
maxEntities: number; | ||
capacity: number; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
toString(): string; | ||
} | ||
@@ -240,22 +201,40 @@ declare class Stats { | ||
maxEntities: number; | ||
_maxLimboEntities: number; | ||
_maxLimboComponents: number; | ||
_numRefs: number; | ||
maxRefs: number; | ||
_maxShapeChangesPerFrame: number; | ||
_maxWritesPerFrame: number; | ||
components: { | ||
[typeName: string]: ComponentStats; | ||
}; | ||
get numEntities(): number; | ||
set numEntities(value: number); | ||
_maxLimboEntities: number; | ||
get maxLimboEntities(): number; | ||
set maxLimboEntities(value: number); | ||
_numRefs: number; | ||
maxRefs: number; | ||
get maxLimboComponents(): number; | ||
set maxLimboComponents(value: number); | ||
get numRefs(): number; | ||
set numRefs(value: number); | ||
_maxShapeChangesPerFrame: number; | ||
get maxShapeChangesPerFrame(): number; | ||
set maxShapeChangesPerFrame(value: number); | ||
_maxWritesPerFrame: number; | ||
get maxWritesPerFrame(): number; | ||
set maxWritesPerFrame(value: number); | ||
for(type: ComponentType<any>): ComponentStats; | ||
toString(): string; | ||
} | ||
type DefsArray = (ComponentType<any> | SystemType | any | DefsArray)[]; | ||
interface WorldOptions { | ||
defs: DefsArray; | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxLimboComponents?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defaultComponentStorage?: ComponentStorage; | ||
} | ||
declare class Dispatcher { | ||
readonly maxEntities: number; | ||
readonly indexer: Indexer; | ||
readonly defaultComponentStorage: ComponentStorage; | ||
readonly registry: Registry; | ||
@@ -272,3 +251,3 @@ readonly systems: SystemBox[]; | ||
private readonly callbackSystem; | ||
constructor({ defs, maxEntities, maxLimboEntities, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame }: WorldOptions); | ||
constructor({ defs, maxEntities, maxLimboEntities, maxLimboComponents, maxRefs, maxShapeChangesPerFrame, maxWritesPerFrame, defaultComponentStorage }: WorldOptions); | ||
private normalizeAndInitSystems; | ||
@@ -299,2 +278,3 @@ private splitDefs; | ||
private readonly shapes; | ||
private readonly staleShapes; | ||
private readonly entityIdPool; | ||
@@ -306,7 +286,13 @@ readonly pool: EntityPool; | ||
private readonly oldDeletionPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
private readonly removalLog; | ||
private readonly prevRemovalPointer; | ||
private readonly oldRemovalPointer; | ||
constructor(maxEntities: number, maxLimboEntities: number, maxLimboComponents: number, types: ComponentType<any>[], dispatcher: Dispatcher); | ||
createEntity(initialComponents: (ComponentType<any> | any)[]): Entity; | ||
queueDeletion(id: EntityId): void; | ||
queueRemoval(id: EntityId, type: ComponentType<any>): void; | ||
flush(): void; | ||
processEndOfFrame(): void; | ||
private processDeletionLog; | ||
private processRemovalLog; | ||
extendMaskAndSetFlag(mask: number[], type: ComponentType<any>): void; | ||
@@ -350,3 +336,4 @@ maskHasFlag(mask: number[] | undefined, type: ComponentType<any>): boolean; | ||
constructor(defaultValue: JSType); | ||
abstract define<C>(binding: Binding<C>, name: string, buffer?: SharedArrayBuffer): SharedArrayBuffer; | ||
abstract defineElastic(binding: Binding<any>, field: Field<any>): void; | ||
abstract defineFixed(binding: Binding<any>, field: Field<any>): void; | ||
static boolean: Type<boolean>; | ||
@@ -364,2 +351,4 @@ static uint8: Type<number>; | ||
static ref: Type<Entity | null>; | ||
static object: Type<any>; | ||
static weakObject: Type<any>; | ||
} | ||
@@ -373,2 +362,8 @@ interface SchemaDef<JSType> { | ||
} | ||
type ComponentStorage = "sparse" | "packed" | "compact"; | ||
interface ComponentOptions { | ||
storage?: ComponentStorage; | ||
capacity?: number; | ||
initialCapacity?: number; | ||
} | ||
interface Field<JSType> { | ||
@@ -378,2 +373,5 @@ name: string; | ||
default: JSType; | ||
buffer?: SharedArrayBuffer; | ||
localBuffer?: any[]; | ||
updateBuffer?(): void; | ||
} | ||
@@ -383,19 +381,27 @@ interface ComponentType<C> { | ||
schema?: Schema; | ||
maxEntities?: number; | ||
__id?: number; | ||
__flagOffset?: number; | ||
__flagMask?: number; | ||
__trackedWrites?: boolean; | ||
__fields?: Field<any>[]; | ||
options?: ComponentOptions; | ||
/** | ||
* A unique, sequential id number for this component type, assigned automatically by becsy. It | ||
* will stay the same across runs as long as the list of defs used to create the world doesn't | ||
* change. Feel free to use this for your own purposes but don't change it. | ||
*/ | ||
id?: number; | ||
__binding?: Binding<C>; | ||
__bind?(id: EntityId, writable: boolean): C; | ||
__create?(id: EntityId): C; | ||
__delete?(id: EntityId): void; | ||
} | ||
declare class Binding<C> { | ||
readonly type: ComponentType<C>; | ||
readonly fields: Field<any>[]; | ||
readonly dispatcher: Dispatcher; | ||
readonly maxEntities: number; | ||
capacity: number; | ||
readonly readonlyInstance: C; | ||
readonly writableInstance: C; | ||
readonly flagOffset: number; | ||
readonly flagMask: number; | ||
trackedWrites: boolean; | ||
entityId: number; | ||
index: number; | ||
constructor(type: ComponentType<C>, dispatcher: Dispatcher, maxEntities: number); | ||
constructor(type: ComponentType<C>, fields: Field<any>[], dispatcher: Dispatcher, capacity: number); | ||
} | ||
@@ -408,3 +414,3 @@ declare class World { | ||
execute(time?: number, delta?: number): void; | ||
get stats(): any; | ||
get stats(): Stats; | ||
} | ||
@@ -418,3 +424,4 @@ interface PropOptions<JSType> { | ||
declare function component(constructor: ComponentType<any>): void; | ||
declare function component(options: ComponentOptions): (constructor: ComponentType<any>) => void; | ||
export { World, Type, Entity, System, SystemType, componentTypes, component, prop, Query, ComponentType }; | ||
//# sourceMappingURL=perf.umd.min.d.ts.map |
@@ -1,2 +0,2 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).becsy={})}(this,(function(e){"use strict";const t=new TextEncoder,s=new TextDecoder;function r(e){throw new Error(`Component is not writable; use entity.write(${e.type.name}) to acquire a writable version`)}class i{defaultValue;constructor(e){this.defaultValue=e}static boolean;static uint8;static int8;static uint16;static int16;static uint32;static int32;static float32;static float64;static staticString;static dynamicString;static ref}class n extends i{NumberArray;constructor(e){super(0),this.NumberArray=e}define(e,t,s){s||(s=new SharedArrayBuffer(e.maxEntities*this.NumberArray.BYTES_PER_ELEMENT));const i=new this.NumberArray(s);return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){i[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){r(e)}}),s}}class a extends i{choices;choicesIndex=new Map;TypedArray;constructor(e){if(super(e[0]),this.choices=e,!e?.length)throw new Error("No choices specified for Type.staticString");e.length<256?this.TypedArray=Uint8Array:e.length<65536?this.TypedArray=Uint16Array:this.TypedArray=Uint32Array;for(let t=0;t<e.length;t++)this.choicesIndex.set(e[t],t)}define(e,t,s){s||(s=new SharedArrayBuffer(e.maxEntities*this.TypedArray.BYTES_PER_ELEMENT));const i=new this.TypedArray(s),n=this.choices,a=this.choicesIndex;return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){const s=a.get(t);if(void 0===s)throw new Error(`Static string not in set: "${t}"`);i[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){r(e)}}),s}}class o extends i{maxUtf8Length;lengthsStride;bytesStride;constructor(e){super(""),this.maxUtf8Length=e+e%2,this.lengthsStride=e/2+1,this.bytesStride=this.maxUtf8Length+2}define(e,i,n){if(!n){const t=e.maxEntities*(this.maxUtf8Length+Uint16Array.BYTES_PER_ELEMENT);n=new SharedArrayBuffer(t)}const a=new Uint16Array(n),o=new Uint8Array(n),h=this.maxUtf8Length,d=this.lengthsStride,c=this.bytesStride;return Object.defineProperty(e.writableInstance,i,{enumerable:!0,configurable:!0,get(){const t=a[e.index*d];return s.decode(new Uint8Array(o.buffer,e.index*c+2,t))},set(s){const r=t.encode(s);if(r.byteLength>h)throw new Error(`Dynamic string length > ${h} after encoding: ${s}`);a[e.index*d]=r.byteLength,o.set(r,e.index*c+2)}}),Object.defineProperty(e.readonlyInstance,i,{enumerable:!0,configurable:!0,get(){const t=a[e.index*d];return s.decode(new Uint8Array(o.buffer,e.index*c+2,t))},set(t){r(e)}}),n}}i.boolean=new class extends i{constructor(){super(!1)}define(e,t,s){s||(s=new SharedArrayBuffer(e.maxEntities));const i=new Uint8Array(s);return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){i[e.index]=t?1:0}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){r(e)}}),s}},i.uint8=new n(Uint8Array),i.int8=new n(Int8Array),i.uint16=new n(Uint16Array),i.int16=new n(Int16Array),i.uint32=new n(Uint32Array),i.int32=new n(Int32Array),i.float32=new n(Float32Array),i.float64=new n(Float64Array),i.staticString=e=>new a(e),i.dynamicString=e=>new o(e),i.ref=new class extends i{constructor(){super(null)}define(e,t,s){s||(s=new SharedArrayBuffer(e.maxEntities*Int32Array.BYTES_PER_ELEMENT));const i=new Int32Array(s);return Object.defineProperty(e.writableInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){const s=i[e.index],r=t?.__id??-1;if(s===r)return;const n=e.dispatcher.indexer;0!==s&&n.remove(s,e.entityId),i[e.index]=r,0!==r&&n.insert(r,e.entityId)}}),Object.defineProperty(e.readonlyInstance,t,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){r(e)}}),s}};class h{type;dispatcher;maxEntities;readonlyInstance;writableInstance;entityId=0;index=0;constructor(e,t,s){this.type=e,this.dispatcher=t,this.maxEntities=s,this.readonlyInstance=new e,this.writableInstance=new e}}function d(e,t,s){const r=Math.min(t.maxEntities??s.maxEntities,s.maxEntities);s.maxEntities,t.__id=e,t.__flagOffset=e>>5,t.__flagMask=1<<(31&e);const n=new h(t,s,r);t.__bind=(e,t)=>(n.entityId=e,n.index=e,t?n.writableInstance:n.readonlyInstance),t.__fields=function(e){const t=e.schema,s=[];for(const e in t){const r=t[e];let n;n=r instanceof i?{name:e,default:r.defaultValue,type:r}:Object.assign({name:e,default:r.type.defaultValue},r),s.push(n)}return s}(t);for(const e of t.__fields)e.type.define(n,e.name)}const c=4194303,l=Object.freeze({});class u{__registry;__id;joined;constructor(e){this.__registry=e}__reset(e){this.__id=e,this.joined=l}add(e,t){return this.__registry.setFlag(this.__id,e),function(e,t,s){const r=e.__bind(t,!0);for(const t of e.__fields)r[t.name]=s?.[t.name]??t.default}(e,this.__id,t),this}addAll(...e){for(let t=0;t<e.length;t++){const s=e[t];let r=e[t+1];"function"==typeof r?r=void 0:t++,this.add(s,r)}return this}remove(e){this.__remove(e)}removeAll(...e){for(const t of e)this.remove(t)}has(e,t=!1){return this.__registry.hasFlag(this.__id,e,t)}read(e){return e.__bind(this.__id,!1)}readRecentlyRemoved(e){return e.__bind(this.__id,!1)}write(e){return e.__trackedWrites&&this.__registry.trackWrite(this.__id,e),e.__bind(this.__id,!0)}delete(){for(const e of this.__registry.types)this.__registry.hasFlag(this.__id,e)&&this.__remove(e);this.__registry.queueDeletion(this.__id),this.__wipeInboundRefs()}__remove(e){this.__deindexOutboundRefs(e),this.__registry.clearFlag(this.__id,e)}__deindexOutboundRefs(e){const t=e.__fields;if(t.some((e=>e.type===i.ref))){const s=this.write(e);for(const e of t)e.type===i.ref&&(s[e.name]=null)}}__wipeInboundRefs(){}__checkMask(e,t){const s=this.__registry.executingSystem?.rwMasks,r=t?s?.write:s?.read;if(r&&!this.__registry.maskHasFlag(r,e))throw new Error(`System didn't mark component ${e.name} as ${t?"writable":"readable"}`)}}class f{maxRefs;buffer;index;numRefs=0;constructor(e){this.maxRefs=e,this.index=new Uint32Array(this.buffer=new SharedArrayBuffer(8*e))}insert(e,t){if(this.numRefs>=this.maxRefs)throw new Error(`Max number of refs reached: ${this.maxRefs}`);const s=this.findIndex(e,t);if(this.index[2*s]===e&&this.index[2*s+1]===t)throw new Error(`Internal error; ref already indexed: ${t} -> ${e}`);this.index.copyWithin(2*(s+1),2*s,2*(this.numRefs+1)),this.index[2*s]=e,this.index[2*s+1]=t,this.numRefs+=1}remove(e,t){const s=this.findIndex(e,t);if(this.index[2*s]===e&&this.index[2*s+1]===t)throw new Error(`Internal error; ref not found: ${t} -> ${e}`);this.numRefs-=1,this.index.copyWithin(s,s+1,this.numRefs-s)}*iterateReferrers(e){}findIndex(e,t){let s=0,r=this.numRefs-1,i=1;for(;s<r;){i=Math.floor((r-s+1)/2)+s;const n=this.index[2*i];n===e?void 0!==t&&this.index[2*i+1]<t?s=i+1:r=i-1:n<e?s=i+1:r=i-1}return s>=r&&(i=Math.max(0,Math.min(this.numRefs+1,s))),i}}class g{size;bytes;constructor(e){this.size=e,this.bytes=new Uint32Array(Math.ceil(e/32))}get(e){return 0!=(this.bytes[e>>>5]&1<<(31&e))}set(e){this.bytes[e>>>5]|=1<<(31&e)}unset(e){this.bytes[e>>>5]&=~(1<<(31&e))}clear(){this.bytes.fill(0)}}const _=[];class m{maxEntries;configParamName;data;corral;constructor(e,t){this.maxEntries=e,this.configParamName=t;const s=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.data=new Uint32Array(s);const r=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.corral=new Uint32Array(r)}push(e){const t=this.corral[0];t&&this.corral[t]===e||(this.corral[t+2]=e,this.corral[0]+=1)}commit(){const e=this.corral[0];if(!e)return;let t=this.data[0];const s=Math.min(e,this.maxEntries-t);for(this.data.set(this.corral.subarray(2,s+2),t+2),s<e&&this.data.set(this.corral.subarray(s+2,e+2),2),t+=e;t>=this.maxEntries;)t-=this.maxEntries,this.data[1]+=1;this.data[0]=t,this.corral[0]=0,this.corral[1]+=1}createPointer(e){return e?(e.index=this.data[0],e.generation=this.data[1],e.corralIndex=this.corral[0],e.corralGeneration=this.corral[1],e):{index:this.data[0],generation:this.data[1],corralIndex:this.corral[0],corralGeneration:this.corral[1]}}hasUpdatesSince(e){return!(e.index===this.data[0]&&e.generation===this.data[1]&&(e.corralGeneration===this.corral[1]?e.corralIndex===this.corral[0]:0===this.corral[0]))}processSince(e,t){let s=_;const r=t?.index??this.data[0],i=t?.generation??this.data[1];if(e.generation===i)if(e.index<r)s=[this.data,e.index+2,r+2],e.index=r;else{const t=this.corral[0],r=this.corral[1];(e.corralGeneration===r?e.corralIndex<t:t)&&(s=[this.corral,e.corralIndex+2,t+2],e.corralIndex=t,e.corralGeneration=r)}else s=[this.data,e.index+2,this.data.length],e.index=0,e.generation=i;return s}countSince(e,t){const s=e.index,r=t?.index??this.data[0],i=t?.generation??this.data[1];return e.index=r,e.generation=i,s===r&&e.generation===i?0:s<r?r-s:this.maxEntries-(s-r)}checkPointers(e,t){this.checkPointer(e),t&&this.checkPointer(t)}checkPointer(e){const t=this.data[0];let s=e.generation;e.index===t?s+1<this.data[1]&&this.throwCapacityExceeded():(e.index>t&&(s+=1),s!==this.data[1]&&this.throwCapacityExceeded())}throwCapacityExceeded(){throw new Error(`Log capacity exceeded, please raise ${this.configParamName} above ${this.maxEntries}`)}}class y{maxItems;configParamName;data;constructor(e,t){this.maxItems=e,this.configParamName=t,this.data=new Uint32Array(new SharedArrayBuffer((e+1)*Uint32Array.BYTES_PER_ELEMENT))}get length(){return this.data[0]}take(){const e=this.data[0]--;return this.data[e]}refill(e){if(!e.length)return;const t=this.length,s=t+e.length;this.data.set(e,t+1),this.data[0]=s}fillWithDescendingIntegers(e){const t=this.length;for(let s=this.data.length-1;s>t;s--)this.data[s]=e++;this.data[0]=this.data.length-1}}class p{pool;entities=[];constructor(e){this.pool=e}add(e){this.entities.push(this.pool.borrowTemporarily(e))}clear(){this.entities.length&&this.entities.splice(0,1/0)}}class w{pool;entities=[];lookupTable;constructor(e,t){this.pool=e,this.lookupTable=new Int32Array(t),this.lookupTable.fill(-1)}add(e){const t=this.entities.push(this.pool.borrow(e))-1;this.lookupTable[e]=t}remove(e){const t=this.lookupTable[e];if(t<0)throw new Error("Internal error, entity not in list");this.pool.return(e),this.lookupTable[e]=-1;const s=this.entities.pop();t<this.entities.length&&(this.entities[t]=s,this.lookupTable[s.__id]=t)}has(e){return this.lookupTable[e]>=0}clear(){throw new Error("Internal error, trying to clear persistent entity list")}}var x;!function(e){e[e.all=1]="all",e[e.added=2]="added",e[e.removed=4]="removed",e[e.changed=8]="changed",e[e.addedOrChanged=16]="addedOrChanged",e[e.changedOrRemoved=32]="changedOrRemoved",e[e.addedChangedOrRemoved=64]="addedChangedOrRemoved"}(x||(x={}));const b=x.added|x.removed|x.changed|x.addedOrChanged|x.changedOrRemoved|x.addedChangedOrRemoved,E=x.changed|x.addedOrChanged|x.changedOrRemoved|x.addedChangedOrRemoved;class v{query;system;results={};flavors=0;__withMask;__withoutMask;__trackMask;__refMask;hasTransientResults;hasChangedResults;currentEntities;changedEntities;constructor(e,t){this.query=e,this.system=t,e.__results=this.results,e.__systemName=t.name}complete(){const e=this.system.dispatcher;this.hasTransientResults=Boolean(this.flavors&b),this.hasChangedResults=Boolean(this.flavors&E),this.flavors&x.all?this.results.all=new w(e.registry.pool,e.maxEntities):this.currentEntities=new g(e.maxEntities),this.hasTransientResults&&this.allocateTransientResultLists(),this.flavors&&this.system.shapeQueries.push(this),this.hasChangedResults&&(this.changedEntities=new g(e.maxEntities),this.system.writeQueries.push(this))}allocateTransientResultLists(){this.flavors&x.added&&this.allocateResult("added"),this.flavors&x.removed&&this.allocateResult("removed"),this.flavors&x.changed&&this.allocateResult("changed"),this.flavors&x.addedOrChanged&&this.allocateResult("addedOrChanged"),this.flavors&x.changedOrRemoved&&this.allocateResult("changedOrRemoved"),this.flavors&x.addedChangedOrRemoved&&this.allocateResult("addedChangedOrRemoved")}allocateResult(e){const t=this.system.dispatcher;this.results[e]=new p(t.registry.pool)}clearTransientResults(){this.hasTransientResults&&(this.results.added?.clear(),this.results.removed?.clear(),this.results.changed?.clear(),this.results.addedOrChanged?.clear(),this.results.changedOrRemoved?.clear(),this.results.addedChangedOrRemoved?.clear(),this.changedEntities?.clear())}handleShapeUpdate(e){const t=this.system.dispatcher.registry,s=this.results.all?.has(e)??this.currentEntities.get(e),r=t.matchShape(e,this.__withMask,this.__withoutMask);r&&!s?(this.currentEntities?.set(e),this.results.all?.add(e),this.results.added?.add(e),this.results.addedOrChanged?.add(e),this.results.addedChangedOrRemoved?.add(e)):!r&&s&&(this.currentEntities?.unset(e),this.system.removedEntities.set(e),this.results.all?.remove(e),this.results.removed?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}handleWrite(e,t,s){!this.changedEntities.get(e)&&(this.__trackMask[t]??0)&s&&(this.changedEntities.set(e),this.results.changed?.add(e),this.results.addedOrChanged?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}}class R{__callback;__userQuery;__query;__system;__lastTypes;constructor(e,t){this.__callback=e,this.__userQuery=t}__build(e){try{this.__system=e,this.__query=new v(this.__userQuery,e),this.__callback(this),this.__query.complete()}catch(t){throw t.message=`Failed to build query in system ${e.name}: ${t.message}`,t}}get and(){return this}get but(){return this}get also(){return this}get all(){return this.__query.flavors|=x.all,this}get added(){return this.__query.flavors|=x.added,this}get removed(){return this.__query.flavors|=x.removed,this}get changed(){return this.__query.flavors|=x.changed,this}get addedOrChanged(){return this.__query.flavors|=x.addedOrChanged,this}get changedOrRemoved(){return this.__query.flavors|=x.changedOrRemoved,this}get addedChangedOrRemoved(){return this.__query.flavors|=x.addedChangedOrRemoved,this}with(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withMask"),this}without(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withoutMask",e),this}using(...e){return this.set(this.__system.rwMasks.read,e),this}get track(){this.set("__trackMask");for(const e of this.__lastTypes)e.__trackedWrites=!0;return this}get read(){return this}get write(){return this.set(this.__system.rwMasks.write),this}set(e,t,s){if(e){if(t||(t=this.__lastTypes),!t)throw new Error("No component type to apply query modifier to");if(this.__lastTypes=t,"string"==typeof e){if(s&&this.__query[e])throw new Error(`Only one ${s} allowed`);this.__query[e]||(this.__query[e]=[]),e=this.__query[e]}else if(s&&e.some((e=>0!==e)))throw new Error(`Only one ${s} allowed`);for(const s of t)this.__system.dispatcher.registry.extendMaskAndSetFlag(e,s)}}}class O{__results;__systemName;get all(){return this.__results.all.entities}get added(){return this.__results.added.entities}get removed(){return this.__results.removed.entities}get changed(){return this.__results.changed.entities}get addedOrChanged(){return this.__results.addedOrChanged.entities}get changedOrRemoved(){return this.__results.changedOrRemoved.entities}get addedChangedOrRemoved(){return this.__results.addedChangedOrRemoved.entities}__checkList(e){if(!this.__results[e])throw new Error(`Query '${e}' not configured, please add .${e} to your query definition in system ${this.__systemName}`)}}class S{__queryBuilders=[];__dispatcher;time;delta;get name(){return this.constructor.name}query(e){const t=new O,s=new R(e,t);return this.__queryBuilders.push(s),t}createEntity(...e){return this.__dispatcher.createEntity(e)}}class k{system;dispatcher;rwMasks={read:[],write:[]};shapeQueries=[];writeQueries=[];hasWriteQueries;processedEntities;removedEntities;shapeLogPointer;writeLogPointer;get name(){return this.system.name}constructor(e,t){this.system=e,this.dispatcher=t,e.__dispatcher=t,this.shapeLogPointer=t.shapeLog.createPointer(),this.writeLogPointer=t.writeLog?.createPointer(),this.processedEntities=new g(t.maxEntities),this.removedEntities=new g(t.maxEntities);for(const t of e.__queryBuilders)t.__build(this);e.__queryBuilders=null,this.hasWriteQueries=!!this.writeQueries.length}execute(e,t){this.system.time=e,this.system.delta=t,this.runQueries(),this.system.execute()}runQueries(){const e=this.dispatcher.shapeLog.hasUpdatesSince(this.shapeLogPointer),t=this.hasWriteQueries&&this.dispatcher.writeLog.hasUpdatesSince(this.writeLogPointer);if(e||t){this.processedEntities.clear();for(const e of this.shapeQueries)e.clearTransientResults();e&&this.__updateShapeQueries(),t&&this.__updateWriteQueries()}}__updateShapeQueries(){const e=this.dispatcher.shapeLog;let t,s,r;for(;[t,s,r]=e.processSince(this.shapeLogPointer),t;)for(let e=s;e<r;e++){const s=t[e];if(!this.processedEntities.get(s)){this.processedEntities.set(s);for(const e of this.shapeQueries)e.handleShapeUpdate(s)}}}__updateWriteQueries(){const e=this.dispatcher.writeLog;let t,s,r;for(;[t,s,r]=e.processSince(this.writeLogPointer),t;)for(let e=s;e<r;e++){const s=t[e],r=4194303&s;if(!this.processedEntities.get(r)){const e=s>>>22;for(const t of this.writeQueries)t.handleWrite(r,e>>5,1<<(31&e))}}}}class P{registry;borrowed;borrowCounts;spares=[];temporarilyBorrowedIds=[];constructor(e,t){this.registry=e,this.borrowed=Array.from({length:t}),this.borrowCounts=new Int32Array(t)}borrow(e){this.borrowCounts[e]+=1;let t=this.borrowed[e];return t||(t=this.borrowed[e]=this.spares.pop()??new u(this.registry),t.__reset(e)),t}borrowTemporarily(e){const t=this.borrow(e);return this.temporarilyBorrowedIds.push(e),t}returnTemporaryBorrows(){for(const e of this.temporarilyBorrowedIds)this.return(e);this.temporarilyBorrowedIds.splice(0,1/0)}return(e){--this.borrowCounts[e]<=0&&(this.spares.push(this.borrowed[e]),this.borrowed[e]=void 0)}}class I{types;dispatcher;stride;shapes;entityIdPool;pool;executingSystem;deletionLog;prevDeletionPointer;oldDeletionPointer;constructor(e,t,s,r){this.types=s,this.dispatcher=r;let i=0;for(const e of s)d(i++,e,this.dispatcher);this.stride=Math.ceil(s.length/32);const n=e*this.stride*4;this.shapes=new Uint32Array(new SharedArrayBuffer(n)),this.entityIdPool=new y(e,"maxEntities"),this.entityIdPool.fillWithDescendingIntegers(0),this.pool=new P(this,e),this.deletionLog=new m(t,"maxLimboEntities"),this.prevDeletionPointer=this.deletionLog.createPointer(),this.oldDeletionPointer=this.deletionLog.createPointer()}createEntity(e){const t=this.entityIdPool.take();this.shapes.fill(0,t*this.stride,(t+1)*this.stride);const s=this.pool.borrowTemporarily(t);return e&&s.addAll(...e),s}queueDeletion(e){this.deletionLog.push(e)}flush(){this.pool.returnTemporaryBorrows(),this.deletionLog.commit()}processEndOfFrame(){this.deletionLog.commit();let e,t,s,r=0;for(;[e,t,s]=this.deletionLog.processSince(this.oldDeletionPointer,this.prevDeletionPointer),e;){const i=e.subarray(t,s);this.entityIdPool.refill(i),r+=i.length}this.deletionLog.createPointer(this.prevDeletionPointer)}extendMaskAndSetFlag(e,t){const s=t.__flagOffset;s>=e.length&&(e.length=s+1,e.fill(0,e.length,s)),e[s]|=t.__flagMask}maskHasFlag(e,t){return 0!=((e?.[t.__flagOffset]??0)&t.__flagMask)}hasFlag(e,t,s=!1){const r=e*this.stride+t.__flagOffset;return 0!=(this.shapes[r]&t.__flagMask)||!(!s||!this.executingSystem?.removedEntities.get(e)||0==((this.executingSystem.rwMasks.read?.[t.__flagOffset]??0)&t.__flagMask))}setFlag(e,t){this.shapes[e*this.stride+t.__flagOffset]|=t.__flagMask,this.dispatcher.shapeLog.push(e)}clearFlag(e,t){this.shapes[e*this.stride+t.__flagOffset]&=~t.__flagMask,this.dispatcher.shapeLog.push(e)}trackWrite(e,t){this.dispatcher.writeLog.push(e|t.__id<<22)}matchShape(e,t,s){const r=e*this.stride;if(t)for(let e=0;e<t.length;e++){const s=t[e];if((this.shapes[r+e]&s)!==s)return!1}if(s)for(let e=0;e<s.length;e++){const t=s[e];if(0!=(this.shapes[r+e]&t))return!1}return!0}}const L="undefined"!=typeof window&&void 0!==window.performance?performance.now.bind(performance):Date.now.bind(Date);class T extends S{__callback;execute(){this.__callback(this)}}class A{maxEntities;indexer;registry;systems;lastTime=L()/1e3;executing;shapeLog;writeLog;shapeLogFramePointer;writeLogFramePointer;stats;userCallbackSystem;callbackSystem;constructor({defs:e,maxEntities:t=1e4,maxLimboEntities:s=Math.ceil(t/5),maxRefs:r=t,maxShapeChangesPerFrame:i=2*t,maxWritesPerFrame:n=4*t}){if(t>c)throw new Error("maxEntities too high, the limit is 4194303");const{componentTypes:a,systemTypes:o}=this.splitDefs(e);if(a.length>1024)throw new Error("Too many component types, the limit is 1024");this.maxEntities=t,this.shapeLog=new m(i,"maxShapeChangesPerFrame"),this.shapeLogFramePointer=this.shapeLog.createPointer(),this.indexer=new f(r),this.registry=new I(t,s,a.flat(1/0),this),this.systems=this.normalizeAndInitSystems(o),this.systems.some((e=>e.hasWriteQueries))&&(this.writeLog=new m(n,"maxWritesPerFrame"),this.writeLogFramePointer=this.writeLog.createPointer()),this.userCallbackSystem=new T,this.callbackSystem=new k(this.userCallbackSystem,this)}normalizeAndInitSystems(e){const t=[],s=e.flat(1/0);for(let e=0;e<s.length;e++){const r=new s[e],i=s[e+1];i&&"function"!=typeof i&&(Object.assign(r,i),e++),t.push(new k(r,this))}return t}splitDefs(e){const t=[],s=[];let r=!1;for(const i of e.flat(1/0))"function"==typeof i?(r=!i.schema,(r?s:t).push(i)):(s.push(i),r=!1);return{componentTypes:t,systemTypes:s}}execute(e,t,s){this.executing=!0,void 0===e&&(e=L()/1e3),void 0===t&&(t=e-this.lastTime),this.lastTime=e;for(const r of s??this.systems)this.registry.executingSystem=r,r.execute(e,t),this.flush();this.registry.executingSystem=void 0,this.registry.processEndOfFrame(),this.executing=!1}executeFunction(e){this.executing=!0,this.userCallbackSystem.__callback=e,this.callbackSystem.execute(0,0),this.flush(),this.registry.processEndOfFrame(),this.executing=!1}gatherFrameStats(){this.stats.frames+=1,this.stats.maxShapeChangesPerFrame=this.shapeLog.countSince(this.shapeLogFramePointer),this.stats.maxWritesPerFrame=this.writeLog?.countSince(this.writeLogFramePointer)??0}flush(){this.registry.flush(),this.shapeLog.commit(),this.writeLog?.commit()}createEntity(e){const t=this.registry.createEntity(e);return this.executing||this.flush(),t}}const C=[];e.Entity=u,e.Query=O,e.System=S,e.Type=i,e.World=class{__dispatcher;constructor(e){this.__dispatcher=new A(e)}build(e){this.__dispatcher.executeFunction(e)}createEntity(...e){this.__dispatcher.createEntity(e)}execute(e,t){this.__dispatcher.execute(e,t)}get stats(){return this.__dispatcher.stats}},e.component=function(e){C.push(e)},e.componentTypes=C,e.prop=function(e){return function(t,s){t.constructor.schema||(t.constructor.schema={});const r="type"in e?e:{type:e};t.constructor.schema[s]=r}},Object.defineProperty(e,"__esModule",{value:!0})})); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).becsy={})}(this,(function(e){"use strict";const t=new TextEncoder,s=new TextDecoder;function r(e){throw new Error(`Component is not writable; use entity.write(${e.type.name}) to acquire a writable version`)}class i{defaultValue;constructor(e){this.defaultValue=e}static boolean;static uint8;static int8;static uint16;static int16;static uint32;static int32;static float32;static float64;static staticString;static dynamicString;static ref;static object;static weakObject}class n extends i{NumberArray;constructor(e){super(0),this.NumberArray=e}defineElastic(e,t){let s,i;t.updateBuffer=()=>{const r=e.capacity*this.NumberArray.BYTES_PER_ELEMENT,n=t.buffer?.byteLength!==r;(n||t.buffer!==s)&&(s=n?new SharedArrayBuffer(r):t.buffer,i=new this.NumberArray(s),n&&t.buffer&&i.set(new this.NumberArray(t.buffer)),t.buffer=s)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){i[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>i[e.index],set(t){r(e)}})}defineFixed(e,t){const s=e.capacity*this.NumberArray.BYTES_PER_ELEMENT,i=new SharedArrayBuffer(s),n=new this.NumberArray(i);t.buffer=i,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>n[e.index],set(t){n[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>n[e.index],set(t){r(e)}})}}class a extends i{choices;choicesIndex=new Map;TypedArray;constructor(e){if(super(e[0]),this.choices=e,!e?.length)throw new Error("No choices specified for Type.staticString");e.length<256?this.TypedArray=Uint8Array:e.length<65536?this.TypedArray=Uint16Array:this.TypedArray=Uint32Array;for(let t=0;t<e.length;t++)this.choicesIndex.set(e[t],t)}defineElastic(e,t){let s,i;const n=this.choices,a=this.choicesIndex;t.updateBuffer=()=>{const r=e.capacity*this.TypedArray.BYTES_PER_ELEMENT,n=t.buffer?.byteLength!==r;(n||t.buffer!==s)&&(s=n?new SharedArrayBuffer(r):t.buffer,i=new this.TypedArray(s),n&&t.buffer&&i.set(new this.TypedArray(t.buffer)),t.buffer=s)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=i[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){const s=a.get(t);if(void 0===s)throw new Error(`Static string not in set: "${t}"`);i[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=i[e.index],s=n[t];if(void 0===s)throw new Error(`Invalid static string index: ${t}`);return s},set(t){r(e)}})}defineFixed(e,t){const s=this.choices,i=this.choicesIndex,n=e.capacity*this.TypedArray.BYTES_PER_ELEMENT,a=new SharedArrayBuffer(n),o=new this.TypedArray(a);t.buffer=a,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=o[e.index],r=s[t];if(void 0===r)throw new Error(`Invalid static string index: ${t}`);return r},set(t){const s=i.get(t);if(void 0===s)throw new Error(`Static string not in set: "${t}"`);o[e.index]=s}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=o[e.index],r=s[t];if(void 0===r)throw new Error(`Invalid static string index: ${t}`);return r},set(t){r(e)}})}}class o extends i{maxUtf8Length;lengthsStride;bytesStride;constructor(e){super(""),this.maxUtf8Length=e+e%2,this.lengthsStride=e/2+1,this.bytesStride=this.maxUtf8Length+2}defineElastic(e,i){let n,a,o;const h=this.maxUtf8Length,d=this.lengthsStride,c=this.bytesStride;i.updateBuffer=()=>{const t=e.capacity*(this.maxUtf8Length+Uint16Array.BYTES_PER_ELEMENT),s=i.buffer?.byteLength!==t;(s||i.buffer!==n)&&(n=s?new SharedArrayBuffer(t):i.buffer,a=new Uint16Array(n),o=new Uint8Array(n),s&&i.buffer&&o.set(new Uint8Array(i.buffer)),i.buffer=n)},i.updateBuffer(),Object.defineProperty(e.writableInstance,i.name,{enumerable:!0,configurable:!0,get(){const t=a[e.index*d];return s.decode(new Uint8Array(o.buffer,e.index*c+2,t))},set(s){const r=t.encode(s);if(r.byteLength>h)throw new Error(`Dynamic string length > ${h} after encoding: ${s}`);a[e.index*d]=r.byteLength,o.set(r,e.index*c+2)}}),Object.defineProperty(e.readonlyInstance,i.name,{enumerable:!0,configurable:!0,get(){const t=a[e.index*d];return s.decode(new Uint8Array(o.buffer,e.index*c+2,t))},set(t){r(e)}})}defineFixed(e,i){const n=this.maxUtf8Length,a=this.lengthsStride,o=this.bytesStride,h=e.capacity*(this.maxUtf8Length+Uint16Array.BYTES_PER_ELEMENT),d=new SharedArrayBuffer(h),c=new Uint16Array(d),l=new Uint8Array(d);i.buffer=d,Object.defineProperty(e.writableInstance,i.name,{enumerable:!0,configurable:!0,get(){const t=c[e.index*a];return s.decode(new Uint8Array(l.buffer,e.index*o+2,t))},set(s){const r=t.encode(s);if(r.byteLength>n)throw new Error(`Dynamic string length > ${n} after encoding: ${s}`);c[e.index*a]=r.byteLength,l.set(r,e.index*o+2)}}),Object.defineProperty(e.readonlyInstance,i.name,{enumerable:!0,configurable:!0,get(){const t=c[e.index*a];return s.decode(new Uint8Array(l.buffer,e.index*o+2,t))},set(t){r(e)}})}}i.boolean=new class extends i{constructor(){super(!1)}defineElastic(e,t){let s,i;t.updateBuffer=()=>{const r=t.buffer?.byteLength!==e.capacity;(r||t.buffer!==s)&&(s=r?new SharedArrayBuffer(e.capacity):t.buffer,i=new Uint8Array(s),r&&t.buffer&&i.set(new Uint8Array(t.buffer)),t.buffer=s)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){i[e.index]=t?1:0}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){r(e)}})}defineFixed(e,t){const s=new SharedArrayBuffer(e.capacity),i=new Uint8Array(s);t.buffer=s,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){i[e.index]=t?1:0}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>Boolean(i[e.index]),set(t){r(e)}})}},i.uint8=new n(Uint8Array),i.int8=new n(Int8Array),i.uint16=new n(Uint16Array),i.int16=new n(Int16Array),i.uint32=new n(Uint32Array),i.int32=new n(Int32Array),i.float32=new n(Float32Array),i.float64=new n(Float64Array),i.staticString=e=>new a(e),i.dynamicString=e=>new o(e),i.ref=new class extends i{constructor(){super(null)}defineElastic(e,t){let s,i;t.updateBuffer=()=>{const r=e.capacity*Int32Array.BYTES_PER_ELEMENT,n=t.buffer?.byteLength!==r;(n||t.buffer!==s)&&(s=n?new SharedArrayBuffer(r):t.buffer,i=new Int32Array(s),n&&t.buffer&&i.set(new Int32Array(t.buffer)),t.buffer=s)},t.updateBuffer(),Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){const s=i[e.index],r=t?.__id??-1;s!==r&&(i[e.index]=r)}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=i[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){r(e)}})}defineFixed(e,t){const s=e.capacity*Int32Array.BYTES_PER_ELEMENT,i=new SharedArrayBuffer(s),n=new Int32Array(i);t.buffer=i,Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=n[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){const s=n[e.index],r=t?.__id??-1;s!==r&&(n[e.index]=r)}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=n[e.index];return-1===t?null:e.dispatcher.registry.pool.borrowTemporarily(t)},set(t){r(e)}})}},i.object=new class extends i{constructor(){super(void 0)}defineElastic(e,t){const s=[];t.localBuffer=s,t.updateBuffer=()=>{},Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>s[e.index],set(t){s[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>s[e.index],set(t){r(e)}})}defineFixed(e,t){const s=new Array(e.capacity);t.localBuffer=s,t.updateBuffer=()=>{},Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get:()=>s[e.index],set(t){s[e.index]=t}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get:()=>s[e.index],set(t){r(e)}})}},i.weakObject=new class extends i{finalizers;constructor(){super(void 0)}defineElastic(e,t){const s=[];t.localBuffer=s,t.updateBuffer=()=>{};const i=this.initFinalizers(e);Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=s[e.index];return null==t?t:t.deref()},set(r){if(null!=r){const s=new WeakRef(r);i?.register(r,{type:e.type,field:t,weakRef:s,id:e.entityId,index:e.index}),r=s}s[e.index]=r}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=s[e.index];return null==t?t:t.deref()},set(t){r(e)}})}defineFixed(e,t){const s=new Array(e.capacity);t.localBuffer=s,t.updateBuffer=()=>{};const i=this.initFinalizers(e);Object.defineProperty(e.writableInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=s[e.index];return null==t?t:t.deref()},set(r){if(null!=r){const s=new WeakRef(r);i?.register(r,{type:e.type,field:t,weakRef:s,id:e.entityId,index:e.index}),r=s}s[e.index]=r}}),Object.defineProperty(e.readonlyInstance,t.name,{enumerable:!0,configurable:!0,get(){const t=s[e.index];return null==t?t:t.deref()},set(t){r(e)}})}initFinalizers(e){if(!e.trackedWrites)return;if(this.finalizers)return this.finalizers;const t=e.dispatcher;return t.writeLog&&"undefined"!=typeof FinalizationRegistry?(this.finalizers=new FinalizationRegistry((({type:e,field:s,weakRef:r,id:i,index:n})=>{s.localBuffer?.[n]===r&&t.registry.trackWrite(i,e)})),this.finalizers):void 0}};class h{type;fields;dispatcher;capacity;readonlyInstance;writableInstance;flagOffset;flagMask;trackedWrites;entityId=0;index=0;constructor(e,t,s,r){this.type=e,this.fields=t,this.dispatcher=s,this.capacity=r,this.readonlyInstance=new e,this.writableInstance=new e,this.flagOffset=e.id>>5,this.flagMask=1<<(31&e.id)}}class d{maxEntities;binding;fields;elastic;index;spares;constructor(e,t,s,r){this.maxEntities=e,this.binding=t,this.fields=s,this.elastic=r,this.growSpares(),this.growCapacity()}acquireIndex(e){let t=this.index[e];return-1===t&&(this.spares[3]>0?t=this.spares[4+--this.spares[3]]:(this.spares[1]===this.spares[2]&&(this.binding.capacity=Math.min(this.maxEntities,2*this.binding.capacity),this.growCapacity()),t=this.spares[1]++),this.index[e]=t),t}releaseIndex(e){this.spares[3]===this.spares.length-4&&this.growSpares(),this.spares[4+this.spares[3]++]=this.index[e],this.index[e]=-1}growCapacity(){const e=this.ArrayType,t=e.BYTES_PER_ELEMENT!==this.spares?.[0];if(!this.index||t){const t=new e(new SharedArrayBuffer(this.maxEntities*e.BYTES_PER_ELEMENT));this.index?t.set(this.index):t.fill(-1),this.index=t}if(this.spares&&t){const t=new e(new SharedArrayBuffer(this.spares.length*e.BYTES_PER_ELEMENT));t.set(this.spares),t[0]=e.BYTES_PER_ELEMENT,this.spares=t}if(this.spares[2]=this.binding.capacity,this.elastic)for(const e of this.fields)e.updateBuffer()}growSpares(){const e=this.ArrayType,t=this.spares?Math.min(this.maxEntities,2*(this.spares.length-4)):8,s=new e(new SharedArrayBuffer((4+t)*e.BYTES_PER_ELEMENT));this.spares?s.set(this.spares):(s[0]=e.BYTES_PER_ELEMENT,s[2]=this.binding.capacity),this.spares=s}get ArrayType(){const e=this.binding.capacity;return e<=127?Int8Array:e<=32767?Int16Array:Int32Array}}function c(e,t,s){const r=t.options?.storage??s.defaultComponentStorage,n="sparse"===r?s.maxEntities:Math.min(s.maxEntities,t.options?.capacity??0),a=t.options?.initialCapacity??8;t.id=e;const o=new h(t,function(e){const t=e.schema,s=[];for(const e in t){const r=t[e];let n;n=r instanceof i?{name:e,default:r.defaultValue,type:r}:Object.assign({name:e,default:r.type.defaultValue},r),s.push(n)}return s}(t),s,n||a);t.__binding=o;for(const e of o.fields)n?e.type.defineFixed(o,e):e.type.defineElastic(o,e);switch(r){case"sparse":t.__bind=(e,t)=>(o.entityId=e,o.index=e,t?o.writableInstance:o.readonlyInstance),t.__create=e=>(o.entityId=e,o.index=e,o.writableInstance);break;case"packed":{const e=new d(s.maxEntities,o,o.fields,!n);t.__bind=(t,s)=>(o.entityId=t,o.index=e.index[t],s?o.writableInstance:o.readonlyInstance),t.__create=t=>(o.entityId=t,o.index=e.acquireIndex(t),o.writableInstance),t.__delete=t=>{e.releaseIndex(t)};break}case"compact":throw new Error("Not yet implemented")}}const l=22,u=4194303,f=u,g=Object.freeze({});class y{__registry;__id;joined;constructor(e){this.__registry=e}__reset(e){this.__id=e,this.joined=g}add(e,t){return this.__registry.setFlag(this.__id,e),function(e,t,s){const r=e.__create(t);for(const t of e.__binding.fields)r[t.name]=s?.[t.name]??t.default}(e,this.__id,t),this}addAll(...e){for(let t=0;t<e.length;t++){const s=e[t];let r=e[t+1];"function"==typeof r?r=void 0:t++,this.add(s,r)}return this}remove(e){this.__remove(e)}removeAll(...e){for(const t of e)this.remove(t)}has(e,t=!1){return this.__registry.hasFlag(this.__id,e,t)}read(e){return e.__bind(this.__id,!1)}readRecentlyRemoved(e){return e.__bind(this.__id,!1)}write(e){return e.__binding.trackedWrites&&this.__registry.trackWrite(this.__id,e),e.__bind(this.__id,!0)}delete(){for(const e of this.__registry.types)this.__registry.hasFlag(this.__id,e)&&this.__remove(e);this.__registry.queueDeletion(this.__id),this.__wipeInboundRefs()}__remove(e){this.__deindexOutboundRefs(e),e.__delete&&this.__registry.queueRemoval(this.__id,e),this.__registry.clearFlag(this.__id,e)}__deindexOutboundRefs(e){const t=e.__binding.fields;if(t.some((e=>e.type===i.ref))){const s=this.write(e);for(const e of t)e.type===i.ref&&(s[e.name]=null)}}__wipeInboundRefs(){}__checkMask(e,t){const s=this.__registry.executingSystem?.rwMasks,r=t?s?.write:s?.read;if(r&&!this.__registry.maskHasFlag(r,e))throw new Error(`System didn't mark component ${e.name} as ${t?"writable":"readable"}`)}}class p{size;bytes;constructor(e){this.size=e,this.bytes=new Uint32Array(Math.ceil(e/32))}get(e){return 0!=(this.bytes[e>>>5]&1<<(31&e))}set(e){this.bytes[e>>>5]|=1<<(31&e)}unset(e){this.bytes[e>>>5]&=~(1<<(31&e))}clear(){this.bytes.fill(0)}}const m=[];class _{maxEntries;configParamName;data;corral;constructor(e,t){this.maxEntries=e,this.configParamName=t;const s=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.data=new Uint32Array(s);const r=new SharedArrayBuffer((e+2)*Uint32Array.BYTES_PER_ELEMENT);this.corral=new Uint32Array(r)}push(e){const t=this.corral[0];t&&this.corral[t]===e||(this.corral[t+2]=e,this.corral[0]+=1)}commit(){const e=this.corral[0];if(!e)return;let t=this.data[0];const s=Math.min(e,this.maxEntries-t);for(this.data.set(this.corral.subarray(2,s+2),t+2),s<e&&this.data.set(this.corral.subarray(s+2,e+2),2),t+=e;t>=this.maxEntries;)t-=this.maxEntries,this.data[1]+=1;this.data[0]=t,this.corral[0]=0,this.corral[1]+=1}createPointer(e){return e?(e.index=this.data[0],e.generation=this.data[1],e.corralIndex=this.corral[0],e.corralGeneration=this.corral[1],e):{index:this.data[0],generation:this.data[1],corralIndex:this.corral[0],corralGeneration:this.corral[1]}}hasUpdatesSince(e){return!(e.index===this.data[0]&&e.generation===this.data[1]&&(e.corralGeneration===this.corral[1]?e.corralIndex===this.corral[0]:0===this.corral[0]))}processSince(e,t){let s=m;const r=t?.index??this.data[0],i=t?.generation??this.data[1];if(e.generation===i)if(e.index<r)s=[this.data,e.index+2,r+2],e.index=r;else{const t=this.corral[0],r=this.corral[1];(e.corralGeneration===r?e.corralIndex<t:t)&&(s=[this.corral,e.corralIndex+2,t+2],e.corralIndex=t,e.corralGeneration=r)}else s=[this.data,e.index+2,this.data.length],e.index=0,e.generation=i;return s}countSince(e,t){const s=e.index,r=t?.index??this.data[0],i=t?.generation??this.data[1];return e.index=r,e.generation=i,s===r&&e.generation===i?0:s<r?r-s:this.maxEntries-(s-r)}checkPointers(e,t){this.checkPointer(e),t&&this.checkPointer(t)}checkPointer(e){const t=this.data[0];let s=e.generation;e.index===t?s+1<this.data[1]&&this.throwCapacityExceeded():(e.index>t&&(s+=1),s!==this.data[1]&&this.throwCapacityExceeded())}throwCapacityExceeded(){throw new Error(`Log capacity exceeded, please raise ${this.configParamName} above ${this.maxEntries}`)}}class b{maxItems;configParamName;data;constructor(e,t){this.maxItems=e,this.configParamName=t,this.data=new Uint32Array(new SharedArrayBuffer((e+1)*Uint32Array.BYTES_PER_ELEMENT))}get length(){return this.data[0]}take(){const e=this.data[0]--;return this.data[e]}refill(e){if(!e.length)return;const t=this.length,s=t+e.length;this.data.set(e,t+1),this.data[0]=s}fillWithDescendingIntegers(e){const t=this.length;for(let s=this.data.length-1;s>t;s--)this.data[s]=e++;this.data[0]=this.data.length-1}}class w{pool;entities=[];constructor(e){this.pool=e}add(e){this.entities.push(this.pool.borrowTemporarily(e))}clear(){this.entities.length&&this.entities.splice(0,1/0)}}class x{pool;entities=[];lookupTable;constructor(e,t){this.pool=e,this.lookupTable=new Int32Array(t),this.lookupTable.fill(-1)}add(e){const t=this.entities.push(this.pool.borrow(e))-1;this.lookupTable[e]=t}remove(e){const t=this.lookupTable[e];if(t<0)throw new Error("Internal error, entity not in list");this.pool.return(e),this.lookupTable[e]=-1;const s=this.entities.pop();t<this.entities.length&&(this.entities[t]=s,this.lookupTable[s.__id]=t)}has(e){return this.lookupTable[e]>=0}clear(){throw new Error("Internal error, trying to clear persistent entity list")}}var E;!function(e){e[e.all=1]="all",e[e.added=2]="added",e[e.removed=4]="removed",e[e.changed=8]="changed",e[e.addedOrChanged=16]="addedOrChanged",e[e.changedOrRemoved=32]="changedOrRemoved",e[e.addedChangedOrRemoved=64]="addedChangedOrRemoved"}(E||(E={}));const v=E.added|E.removed|E.changed|E.addedOrChanged|E.changedOrRemoved|E.addedChangedOrRemoved,P=E.changed|E.addedOrChanged|E.changedOrRemoved|E.addedChangedOrRemoved;class S{query;system;results={};flavors=0;__withMask;__withoutMask;__trackMask;__refMask;hasTransientResults;hasChangedResults;currentEntities;changedEntities;constructor(e,t){this.query=e,this.system=t,e.__results=this.results,e.__systemName=t.name}complete(){const e=this.system.dispatcher;this.hasTransientResults=Boolean(this.flavors&v),this.hasChangedResults=Boolean(this.flavors&P),this.flavors&E.all?this.results.all=new x(e.registry.pool,e.maxEntities):this.currentEntities=new p(e.maxEntities),this.hasTransientResults&&this.allocateTransientResultLists(),this.flavors&&this.system.shapeQueries.push(this),this.hasChangedResults&&(this.changedEntities=new p(e.maxEntities),this.system.writeQueries.push(this))}allocateTransientResultLists(){this.flavors&E.added&&this.allocateResult("added"),this.flavors&E.removed&&this.allocateResult("removed"),this.flavors&E.changed&&this.allocateResult("changed"),this.flavors&E.addedOrChanged&&this.allocateResult("addedOrChanged"),this.flavors&E.changedOrRemoved&&this.allocateResult("changedOrRemoved"),this.flavors&E.addedChangedOrRemoved&&this.allocateResult("addedChangedOrRemoved")}allocateResult(e){const t=this.system.dispatcher;this.results[e]=new w(t.registry.pool)}clearTransientResults(){this.hasTransientResults&&(this.results.added?.clear(),this.results.removed?.clear(),this.results.changed?.clear(),this.results.addedOrChanged?.clear(),this.results.changedOrRemoved?.clear(),this.results.addedChangedOrRemoved?.clear(),this.changedEntities?.clear())}handleShapeUpdate(e){const t=this.system.dispatcher.registry,s=this.results.all?.has(e)??this.currentEntities.get(e),r=t.matchShape(e,this.__withMask,this.__withoutMask);r&&!s?(this.currentEntities?.set(e),this.results.all?.add(e),this.results.added?.add(e),this.results.addedOrChanged?.add(e),this.results.addedChangedOrRemoved?.add(e)):!r&&s&&(this.currentEntities?.unset(e),this.results.all?.remove(e),this.results.removed?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}handleWrite(e,t,s){!this.changedEntities.get(e)&&(this.__trackMask[t]??0)&s&&(this.changedEntities.set(e),this.results.changed?.add(e),this.results.addedOrChanged?.add(e),this.results.changedOrRemoved?.add(e),this.results.addedChangedOrRemoved?.add(e))}}class L{__callback;__userQuery;__query;__system;__lastTypes;constructor(e,t){this.__callback=e,this.__userQuery=t}__build(e){try{this.__system=e,this.__query=new S(this.__userQuery,e),this.__callback(this),this.__query.complete()}catch(t){throw t.message=`Failed to build query in system ${e.name}: ${t.message}`,t}}get and(){return this}get but(){return this}get also(){return this}get all(){return this.__query.flavors|=E.all,this}get added(){return this.__query.flavors|=E.added,this}get removed(){return this.__query.flavors|=E.removed,this}get changed(){return this.__query.flavors|=E.changed,this}get addedOrChanged(){return this.__query.flavors|=E.addedOrChanged,this}get changedOrRemoved(){return this.__query.flavors|=E.changedOrRemoved,this}get addedChangedOrRemoved(){return this.__query.flavors|=E.addedChangedOrRemoved,this}with(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withMask"),this}without(...e){return this.set(this.__system.rwMasks.read,e),this.set("__withoutMask",e),this}using(...e){return this.set(this.__system.rwMasks.read,e),this}get track(){this.set("__trackMask");for(const e of this.__lastTypes)e.__binding.trackedWrites=!0;return this}get read(){return this}get write(){return this.set(this.__system.rwMasks.write),this}set(e,t,s){if(e){if(t||(t=this.__lastTypes),!t)throw new Error("No component type to apply query modifier to");if(this.__lastTypes=t,"string"==typeof e){if(s&&this.__query[e])throw new Error(`Only one ${s} allowed`);this.__query[e]||(this.__query[e]=[]),e=this.__query[e]}else if(s&&e.some((e=>0!==e)))throw new Error(`Only one ${s} allowed`);for(const s of t)this.__system.dispatcher.registry.extendMaskAndSetFlag(e,s)}}}class I{__results;__systemName;get all(){return this.__results.all.entities}get added(){return this.__results.added.entities}get removed(){return this.__results.removed.entities}get changed(){return this.__results.changed.entities}get addedOrChanged(){return this.__results.addedOrChanged.entities}get changedOrRemoved(){return this.__results.changedOrRemoved.entities}get addedChangedOrRemoved(){return this.__results.addedChangedOrRemoved.entities}__checkList(e){if(!this.__results[e])throw new Error(`Query '${e}' not configured, please add .${e} to your query definition in system ${this.__systemName}`)}}class A{__queryBuilders=[];__dispatcher;time;delta;get name(){return this.constructor.name}query(e){const t=new I,s=new L(e,t);return this.__queryBuilders.push(s),t}createEntity(...e){return this.__dispatcher.createEntity(e)}}class T{system;dispatcher;rwMasks={read:[],write:[]};shapeQueries=[];writeQueries=[];hasWriteQueries;processedEntities;shapeLogPointer;writeLogPointer;get name(){return this.system.name}constructor(e,t){this.system=e,this.dispatcher=t,e.__dispatcher=t,this.shapeLogPointer=t.shapeLog.createPointer(),this.writeLogPointer=t.writeLog?.createPointer(),this.processedEntities=new p(t.maxEntities);for(const t of e.__queryBuilders)t.__build(this);e.__queryBuilders=null,this.hasWriteQueries=!!this.writeQueries.length}execute(e,t){this.system.time=e,this.system.delta=t,this.runQueries(),this.system.execute()}runQueries(){const e=this.dispatcher.shapeLog.hasUpdatesSince(this.shapeLogPointer),t=this.hasWriteQueries&&this.dispatcher.writeLog.hasUpdatesSince(this.writeLogPointer);if(e||t){this.processedEntities.clear();for(const e of this.shapeQueries)e.clearTransientResults();e&&this.__updateShapeQueries(),t&&this.__updateWriteQueries()}}__updateShapeQueries(){const e=this.dispatcher.shapeLog;let t,s,r;for(;[t,s,r]=e.processSince(this.shapeLogPointer),t;)for(let e=s;e<r;e++){const s=t[e];if(!this.processedEntities.get(s)){this.processedEntities.set(s);for(const e of this.shapeQueries)e.handleShapeUpdate(s)}}}__updateWriteQueries(){const e=this.dispatcher.writeLog;let t,s,r;for(;[t,s,r]=e.processSince(this.writeLogPointer),t;)for(let e=s;e<r;e++){const s=t[e],r=s&f;if(!this.processedEntities.get(r)){const e=s>>>l;for(const t of this.writeQueries)t.handleWrite(r,e>>5,1<<(31&e))}}}}class O{registry;borrowed;borrowCounts;spares=[];temporarilyBorrowedIds=[];constructor(e,t){this.registry=e,this.borrowed=Array.from({length:t}),this.borrowCounts=new Int32Array(t)}borrow(e){this.borrowCounts[e]+=1;let t=this.borrowed[e];return t||(t=this.borrowed[e]=this.spares.pop()??new y(this.registry),t.__reset(e)),t}borrowTemporarily(e){const t=this.borrow(e);return this.temporarilyBorrowedIds.push(e),t}returnTemporaryBorrows(){for(const e of this.temporarilyBorrowedIds)this.return(e);this.temporarilyBorrowedIds.splice(0,1/0)}return(e){--this.borrowCounts[e]<=0&&(this.spares.push(this.borrowed[e]),this.borrowed[e]=void 0)}}class R{types;dispatcher;stride;shapes;staleShapes;entityIdPool;pool;executingSystem;deletionLog;prevDeletionPointer;oldDeletionPointer;removalLog;prevRemovalPointer;oldRemovalPointer;constructor(e,t,s,r,i){this.types=r,this.dispatcher=i;let n=0;for(const e of r)c(n++,e,this.dispatcher);this.stride=Math.ceil(r.length/32);const a=e*this.stride*4;this.shapes=new Uint32Array(new SharedArrayBuffer(a)),this.staleShapes=new Uint32Array(new SharedArrayBuffer(a)),this.entityIdPool=new b(e,"maxEntities"),this.entityIdPool.fillWithDescendingIntegers(0),this.pool=new O(this,e),this.deletionLog=new _(t,"maxLimboEntities"),this.prevDeletionPointer=this.deletionLog.createPointer(),this.oldDeletionPointer=this.deletionLog.createPointer(),this.removalLog=new _(s,"maxLimboComponents"),this.prevRemovalPointer=this.removalLog.createPointer(),this.oldRemovalPointer=this.removalLog.createPointer()}createEntity(e){const t=this.entityIdPool.take();this.shapes.fill(0,t*this.stride,(t+1)*this.stride);const s=this.pool.borrowTemporarily(t);return e&&s.addAll(...e),s}queueDeletion(e){this.deletionLog.push(e)}queueRemoval(e,t){this.removalLog.push(e|t.id<<l)}flush(){this.pool.returnTemporaryBorrows(),this.deletionLog.commit(),this.removalLog.commit()}processEndOfFrame(){this.processDeletionLog(),this.processRemovalLog()}processDeletionLog(){this.deletionLog.commit();let e,t,s,r=0;for(;[e,t,s]=this.deletionLog.processSince(this.oldDeletionPointer,this.prevDeletionPointer),e;){const i=e.subarray(t,s);this.entityIdPool.refill(i),r+=i.length}this.deletionLog.createPointer(this.prevDeletionPointer)}processRemovalLog(){let e,t,s;for(this.removalLog.commit();[e,t,s]=this.removalLog.processSince(this.oldRemovalPointer,this.prevRemovalPointer),e;)for(let r=t;r<s;r++){const t=e[r],s=t&f,i=t>>>l,n=this.types[i],a=s*this.stride+n.__binding.flagOffset,o=n.__binding.flagMask;0==(this.shapes[a]&o)&&(this.staleShapes[a]&=~o,n.__delete(s))}this.removalLog.createPointer(this.prevRemovalPointer)}extendMaskAndSetFlag(e,t){const s=t.__binding.flagOffset;s>=e.length&&(e.length=s+1,e.fill(0,e.length,s)),e[s]|=t.__binding.flagMask}maskHasFlag(e,t){return 0!=((e?.[t.__binding.flagOffset]??0)&t.__binding.flagMask)}hasFlag(e,t,s=!1){const r=e*this.stride+t.__binding.flagOffset,i=t.__binding.flagMask;return 0!=(this.shapes[r]&i)||!(!s||0==(this.staleShapes[r]&i))}setFlag(e,t){const s=e*this.stride+t.__binding.flagOffset,r=t.__binding.flagMask;this.shapes[s]|=r,this.staleShapes[s]|=r,this.dispatcher.shapeLog.push(e)}clearFlag(e,t){this.shapes[e*this.stride+t.__binding.flagOffset]&=~t.__binding.flagMask,this.dispatcher.shapeLog.push(e)}trackWrite(e,t){this.dispatcher.writeLog.push(e|t.id<<l)}matchShape(e,t,s){const r=e*this.stride;if(t)for(let e=0;e<t.length;e++){const s=t[e];if((this.shapes[r+e]&s)!==s)return!1}if(s)for(let e=0;e<s.length;e++){const t=s[e];if(0!=(this.shapes[r+e]&t))return!1}return!0}}const k="undefined"!=typeof window&&void 0!==window.performance?performance.now.bind(performance):Date.now.bind(Date);class B extends A{__callback;execute(){this.__callback(this)}}class C{maxEntities;defaultComponentStorage;registry;systems;lastTime=k()/1e3;executing;shapeLog;writeLog;shapeLogFramePointer;writeLogFramePointer;stats;userCallbackSystem;callbackSystem;constructor({defs:e,maxEntities:t=1e4,maxLimboEntities:s=Math.ceil(t/5),maxLimboComponents:r=Math.ceil(t/5),maxRefs:i=t,maxShapeChangesPerFrame:n=2*t,maxWritesPerFrame:a=4*t,defaultComponentStorage:o="sparse"}){if(t>u)throw new Error("maxEntities too high, the limit is 4194303");const{componentTypes:h,systemTypes:d}=this.splitDefs(e);if(h.length>1024)throw new Error("Too many component types, the limit is 1024");this.maxEntities=t,this.defaultComponentStorage=o,this.shapeLog=new _(n,"maxShapeChangesPerFrame"),this.shapeLogFramePointer=this.shapeLog.createPointer(),this.registry=new R(t,s,r,h.flat(1/0),this),this.systems=this.normalizeAndInitSystems(d),this.systems.some((e=>e.hasWriteQueries))&&(this.writeLog=new _(a,"maxWritesPerFrame"),this.writeLogFramePointer=this.writeLog.createPointer()),this.userCallbackSystem=new B,this.callbackSystem=new T(this.userCallbackSystem,this)}normalizeAndInitSystems(e){const t=[],s=e.flat(1/0);for(let e=0;e<s.length;e++){const r=new s[e],i=s[e+1];i&&"function"!=typeof i&&(Object.assign(r,i),e++),t.push(new T(r,this))}return t}splitDefs(e){const t=[],s=[];let r=!1;for(const i of e.flat(1/0))"function"==typeof i?(r=!i.schema,(r?s:t).push(i)):(s.push(i),r=!1);return{componentTypes:t,systemTypes:s}}execute(e,t,s){this.executing=!0,void 0===e&&(e=k()/1e3),void 0===t&&(t=e-this.lastTime),this.lastTime=e;for(const r of s??this.systems)this.registry.executingSystem=r,r.execute(e,t),this.flush();this.registry.executingSystem=void 0,this.registry.processEndOfFrame(),this.executing=!1}executeFunction(e){this.executing=!0,this.userCallbackSystem.__callback=e,this.callbackSystem.execute(0,0),this.flush(),this.registry.processEndOfFrame(),this.executing=!1}gatherFrameStats(){this.stats.frames+=1,this.stats.maxShapeChangesPerFrame=this.shapeLog.countSince(this.shapeLogFramePointer),this.stats.maxWritesPerFrame=this.writeLog?.countSince(this.writeLogFramePointer)??0}flush(){this.registry.flush(),this.shapeLog.commit(),this.writeLog?.commit()}createEntity(e){const t=this.registry.createEntity(e);return this.executing||this.flush(),t}}const M=[];e.Entity=y,e.Query=I,e.System=A,e.Type=i,e.World=class{__dispatcher;constructor(e){this.__dispatcher=new C(e)}build(e){this.__dispatcher.executeFunction(e)}createEntity(...e){this.__dispatcher.createEntity(e)}execute(e,t){this.__dispatcher.execute(e,t)}get stats(){return this.__dispatcher.stats}},e.component=function(e){if("function"!=typeof e)return t=>{t.options=e,M.push(t)};M.push(e)},e.componentTypes=M,e.prop=function(e){return function(t,s){t.constructor.schema||(t.constructor.schema={});const r="type"in e?e:{type:e};t.constructor.schema[s]=r}},Object.defineProperty(e,"__esModule",{value:!0})})); | ||
//# sourceMappingURL=perf.umd.min.js.map |
@@ -18,2 +18,4 @@ # becsy | ||
Then we add: | ||
- [ ] selectable component storage strategies, to adjust the performance/memory trade-off | ||
- [ ] weak references to native JS objects, for better integration with other frameworks | ||
- [ ] entity references that can be traversed in either direction | ||
@@ -20,0 +22,0 @@ - [ ] declarative system ordering based on data dependencies |
@@ -15,2 +15,10 @@ import {Type} from './type'; | ||
export type ComponentStorage = 'sparse' | 'packed' | 'compact'; | ||
export interface ComponentOptions { | ||
storage?: ComponentStorage; | ||
capacity?: number; | ||
initialCapacity?: number; | ||
} | ||
export interface Field<JSType> { | ||
@@ -20,2 +28,5 @@ name: string; | ||
default: JSType; | ||
buffer?: SharedArrayBuffer; | ||
localBuffer?: any[]; | ||
updateBuffer?(): void; | ||
} | ||
@@ -26,9 +37,15 @@ | ||
schema?: Schema; | ||
maxEntities?: number; | ||
__id?: number; | ||
__flagOffset?: number; | ||
__flagMask?: number; | ||
__trackedWrites?: boolean; | ||
__fields?: Field<any>[]; | ||
options?: ComponentOptions; | ||
/** | ||
* A unique, sequential id number for this component type, assigned automatically by becsy. It | ||
* will stay the same across runs as long as the list of defs used to create the world doesn't | ||
* change. Feel free to use this for your own purposes but don't change it. | ||
*/ | ||
id?: number; | ||
__binding?: Binding<C>; | ||
__bind?(id: EntityId, writable: boolean): C; | ||
__create?(id: EntityId): C; | ||
__delete?(id: EntityId): void; | ||
} | ||
@@ -39,2 +56,5 @@ | ||
readonly writableInstance: C; | ||
readonly flagOffset: number; | ||
readonly flagMask: number; | ||
trackedWrites: boolean; | ||
entityId = 0; | ||
@@ -44,6 +64,9 @@ index = 0; | ||
constructor( | ||
readonly type: ComponentType<C>, readonly dispatcher: Dispatcher, readonly maxEntities: number | ||
readonly type: ComponentType<C>, readonly fields: Field<any>[], readonly dispatcher: Dispatcher, | ||
public capacity: number | ||
) { | ||
this.readonlyInstance = new type(); // eslint-disable-line new-cap | ||
this.writableInstance = new type(); // eslint-disable-line new-cap | ||
this.flagOffset = type.id! >> 5; | ||
this.flagMask = 1 << (type.id! & 31); | ||
} | ||
@@ -53,2 +76,95 @@ } | ||
interface Storage { | ||
acquireIndex(id: EntityId): number; | ||
releaseIndex(id: EntityId): void; | ||
} | ||
class PackedStorage implements Storage { | ||
index: Int8Array | Int16Array | Int32Array; | ||
// layout: bytesPerElement, nextIndex, capacity, numSpares, ...spareIndices | ||
private spares: Int8Array | Int16Array | Int32Array; | ||
constructor( | ||
private readonly maxEntities: number, private readonly binding: Binding<any>, | ||
private readonly fields: Field<any>[], private readonly elastic: boolean | ||
) { | ||
this.growSpares(); | ||
this.growCapacity(); | ||
} | ||
acquireIndex(id: number): number { | ||
let index = this.index[id]; | ||
if (index === -1) { | ||
if (this.spares[3] > 0) { | ||
index = this.spares[--this.spares[3] + 4]; | ||
} else { | ||
if (this.spares[1] === this.spares[2]) { | ||
CHECK: if (!this.elastic) { | ||
throw new Error( | ||
`Storage exhausted for component ${this.binding.type.name}; ` + | ||
`raise its capacity above ${this.binding.capacity}`); | ||
} | ||
this.binding.capacity = Math.min(this.maxEntities, this.binding.capacity * 2); | ||
this.growCapacity(); | ||
} | ||
index = this.spares[1]++; | ||
} | ||
this.index[id] = index; | ||
} | ||
return index; | ||
} | ||
releaseIndex(id: number): void { | ||
DEBUG: if (this.index[id] === -1) { | ||
throw new Error(`Internal error, index for entity ${id} not allocated`); | ||
} | ||
if (this.spares[3] === this.spares.length - 4) this.growSpares(); | ||
this.spares[this.spares[3]++ + 4] = this.index[id]; | ||
this.index[id] = -1; | ||
} | ||
private growCapacity(): void { | ||
STATS: this.binding.dispatcher.stats.for(this.binding.type).capacity = this.binding.capacity; | ||
const ArrayType = this.ArrayType; | ||
const elementSizeChanged = ArrayType.BYTES_PER_ELEMENT !== this.spares?.[0]; | ||
if (!this.index || elementSizeChanged) { | ||
const buffer = new SharedArrayBuffer(this.maxEntities * ArrayType.BYTES_PER_ELEMENT); | ||
const newIndex = new ArrayType(buffer); | ||
if (this.index) newIndex.set(this.index); else newIndex.fill(-1); | ||
this.index = newIndex; | ||
} | ||
if (this.spares && elementSizeChanged) { | ||
const buffer = new SharedArrayBuffer(this.spares.length * ArrayType.BYTES_PER_ELEMENT); | ||
const newSpares = new ArrayType(buffer); | ||
newSpares.set(this.spares); | ||
newSpares[0] = ArrayType.BYTES_PER_ELEMENT; | ||
this.spares = newSpares; | ||
} | ||
this.spares[2] = this.binding.capacity; | ||
if (this.elastic) for (const field of this.fields) field.updateBuffer!(); | ||
} | ||
private growSpares(): void { | ||
const ArrayType = this.ArrayType; | ||
const maxSpares = this.spares ? Math.min(this.maxEntities, (this.spares.length - 4) * 2) : 8; | ||
const sparesBuffer = new SharedArrayBuffer((4 + maxSpares) * ArrayType.BYTES_PER_ELEMENT); | ||
const newSpares = new ArrayType(sparesBuffer); | ||
if (this.spares) { | ||
newSpares.set(this.spares); | ||
} else { | ||
newSpares[0] = ArrayType.BYTES_PER_ELEMENT; | ||
newSpares[2] = this.binding.capacity; | ||
} | ||
this.spares = newSpares; | ||
} | ||
private get ArrayType() { | ||
const capacity = this.binding.capacity; | ||
return capacity <= (1 << 7) - 1 ? Int8Array : | ||
capacity <= (1 << 15) - 1 ? Int16Array : Int32Array; | ||
} | ||
} | ||
export function initComponent(type: ComponentType<any>, id: EntityId, values: any): void { | ||
@@ -64,5 +180,4 @@ CHECK: { | ||
} | ||
// TODO: in packed array mode, allocate a new index | ||
const component = type.__bind!(id, true); | ||
for (const field of type.__fields!) { | ||
const component = type.__create!(id); | ||
for (const field of type.__binding!.fields) { | ||
(component as any)[field.name] = values?.[field.name] ?? field.default; | ||
@@ -90,10 +205,24 @@ } | ||
export function decorateComponentType<C>( | ||
export function assimilateComponentType<C>( | ||
typeId: number, type: ComponentType<C>, dispatcher: Dispatcher | ||
): void { | ||
const storage = type.options?.storage ?? dispatcher.defaultComponentStorage; | ||
const capacity = storage === 'sparse' ? | ||
dispatcher.maxEntities : Math.min(dispatcher.maxEntities, type.options?.capacity ?? 0); | ||
const initialCapacity = type.options?.initialCapacity ?? 8; | ||
CHECK: { | ||
if ((type.maxEntities ?? 0) > dispatcher.maxEntities) { | ||
throw new Error( | ||
`Component type ${type.name} maxEntities higher than world maxEntities; ` + | ||
`reduce ${type.maxEntities} to or below ${dispatcher.maxEntities}`); | ||
if (typeof type.options?.capacity !== 'undefined') { | ||
if (storage === 'sparse') { | ||
throw new Error( | ||
`Component type ${type.name} cannot combine custom capacity with sparse storage` | ||
); | ||
} | ||
if (type.options.capacity <= 0) { | ||
throw new Error( | ||
`Component type ${type.name} capacity option must be great than zero: got ${capacity}`); | ||
} | ||
if (typeof type.options.initialCapacity !== 'undefined') { | ||
throw new Error( | ||
`Component type ${type.name} cannot have both capacity and initialCapacity options`); | ||
} | ||
} | ||
@@ -104,18 +233,58 @@ if ((typeof process === 'undefined' || process.env.NODE_ENV !== 'test') && type.__bind) { | ||
} | ||
const maxEntities = Math.min(type.maxEntities ?? dispatcher.maxEntities, dispatcher.maxEntities); | ||
if (maxEntities < dispatcher.maxEntities) { | ||
// TODO: enable packed array mode | ||
type.id = typeId; | ||
const binding = new Binding<C>(type, gatherFields(type), dispatcher, capacity || initialCapacity); | ||
type.__binding = binding; | ||
for (const field of binding.fields) { | ||
if (capacity) { | ||
field.type.defineFixed(binding, field); | ||
} else { | ||
field.type.defineElastic(binding, field); | ||
} | ||
} | ||
type.__id = typeId; | ||
type.__flagOffset = typeId >> 5; | ||
type.__flagMask = 1 << (typeId & 31); | ||
const binding = new Binding<C>(type, dispatcher, maxEntities); | ||
type.__bind = (id: EntityId, writable: boolean) => { | ||
binding.entityId = id; | ||
binding.index = id; | ||
return writable ? binding.writableInstance : binding.readonlyInstance; | ||
}; | ||
type.__fields = gatherFields(type); | ||
for (const field of type.__fields!) field.type.define(binding, field.name); | ||
switch (storage) { | ||
case 'sparse': | ||
// Inline the trivial storage manager for performance. | ||
STATS: dispatcher.stats.for(type).capacity = capacity; // fixed | ||
type.__bind = (id: EntityId, writable: boolean): C => { | ||
binding.entityId = id; | ||
binding.index = id; | ||
return writable ? binding.writableInstance : binding.readonlyInstance; | ||
}; | ||
type.__create = (id: EntityId): C => { | ||
binding.entityId = id; | ||
binding.index = id; | ||
return binding.writableInstance; | ||
}; | ||
break; | ||
case 'packed': { | ||
const storageManager = | ||
new PackedStorage(dispatcher.maxEntities, binding, binding.fields, !capacity); | ||
type.__bind = (id: EntityId, writable: boolean): C => { | ||
binding.entityId = id; | ||
binding.index = storageManager.index[id]; | ||
DEBUG: if (binding.index === -1) { | ||
throw new Error(`Attempt to bind unacquired entity ${id} to ${type.name}`); | ||
} | ||
return writable ? binding.writableInstance : binding.readonlyInstance; | ||
}; | ||
type.__create = (id: EntityId): C => { | ||
binding.entityId = id; | ||
binding.index = storageManager.acquireIndex(id); | ||
return binding.writableInstance; | ||
}; | ||
type.__delete = (id: EntityId): void => { | ||
storageManager.releaseIndex(id); | ||
}; | ||
break; | ||
} | ||
case 'compact': | ||
throw new Error('Not yet implemented'); | ||
default: | ||
CHECK: throw new Error(`Invalid storage type "${storage}`); | ||
} | ||
} | ||
@@ -1,2 +0,2 @@ | ||
import type {ComponentType} from './component'; | ||
import type {ComponentOptions, ComponentType} from './component'; | ||
import type {Type} from './type'; | ||
@@ -22,4 +22,14 @@ | ||
export function component(constructor: ComponentType<any>): void { | ||
componentTypes.push(constructor); | ||
export function component(constructor: ComponentType<any>): void; | ||
export function component(options: ComponentOptions): (constructor: ComponentType<any>) => void; | ||
export function component(arg: ComponentType<any> | ComponentOptions): | ||
((constructor: ComponentType<any>) => void) | void { | ||
if (typeof arg === 'function') { | ||
componentTypes.push(arg); | ||
} else { | ||
return (constructor: ComponentType<any>) => { | ||
constructor.options = arg; | ||
componentTypes.push(constructor); | ||
}; | ||
} | ||
} |
@@ -1,7 +0,7 @@ | ||
import type {ComponentType} from './component'; | ||
import type {ComponentStorage, ComponentType} from './component'; | ||
import {Entity, MAX_NUM_COMPONENTS, MAX_NUM_ENTITIES} from './entity'; | ||
import {Indexer} from './indexer'; | ||
import {Log, LogPointer} from './datastructures'; | ||
import {System, SystemBox, SystemType} from './system'; | ||
import {Registry} from './registry'; | ||
import {Stats} from './stats'; | ||
@@ -16,80 +16,12 @@ | ||
export interface WorldOptions { | ||
defs: DefsArray; | ||
maxEntities?: number; | ||
maxLimboEntities?: number; | ||
maxLimboComponents?: number; | ||
maxRefs?: number; | ||
maxShapeChangesPerFrame?: number; | ||
maxWritesPerFrame?: number; | ||
defs: DefsArray; | ||
defaultComponentStorage?: ComponentStorage; | ||
} | ||
export class Stats { | ||
frames = 0; | ||
_numEntities = 0; | ||
maxEntities = 0; | ||
get numEntities(): number { | ||
return this._numEntities; | ||
} | ||
set numEntities(value: number) { | ||
this._numEntities = value; | ||
if (value > this.maxEntities) this.maxEntities = value; | ||
} | ||
_maxLimboEntities = 0; | ||
get maxLimboEntities(): number { | ||
return this._maxLimboEntities; | ||
} | ||
set maxLimboEntities(value: number) { | ||
if (value > this._maxLimboEntities) this._maxLimboEntities = value; | ||
} | ||
_numRefs = 0; | ||
maxRefs = 0; | ||
get numRefs(): number { | ||
return this._numRefs; | ||
} | ||
set numRefs(value: number) { | ||
this._numRefs = value; | ||
if (value > this.maxRefs) this.maxRefs = value; | ||
} | ||
_maxShapeChangesPerFrame = 0; | ||
get maxShapeChangesPerFrame(): number { | ||
return this._maxShapeChangesPerFrame; | ||
} | ||
set maxShapeChangesPerFrame(value: number) { | ||
if (value > this._maxShapeChangesPerFrame) this._maxShapeChangesPerFrame = value; | ||
} | ||
_maxWritesPerFrame = 0; | ||
get maxWritesPerFrame(): number { | ||
return this._maxWritesPerFrame; | ||
} | ||
set maxWritesPerFrame(value: number) { | ||
if (value > this._maxWritesPerFrame) this._maxWritesPerFrame = value; | ||
} | ||
toString(): string { | ||
/* eslint-disable max-len */ | ||
return `World stats: | ||
frames: ${this.frames} | ||
entities: ${this.numEntities} of ${this.maxEntities} max (${this.maxLimboEntities} limbo max) | ||
refs: ${this.numRefs} of ${this.maxRefs} max | ||
logs: ${this.maxShapeChangesPerFrame} shape changes/frame max, ${this.maxWritesPerFrame} writes/frame max`; | ||
/* eslint-enable max-len */ | ||
} | ||
} | ||
class CallbackSystem extends System { | ||
@@ -106,3 +38,3 @@ __callback: (system: System) => void; | ||
readonly maxEntities; | ||
readonly indexer; | ||
readonly defaultComponentStorage; | ||
readonly registry; | ||
@@ -124,5 +56,7 @@ readonly systems: SystemBox[]; | ||
maxLimboEntities = Math.ceil(maxEntities / 5), | ||
maxLimboComponents = Math.ceil(maxEntities / 5), | ||
maxRefs = maxEntities, | ||
maxShapeChangesPerFrame = maxEntities * 2, | ||
maxWritesPerFrame = maxEntities * 4, | ||
defaultComponentStorage = 'sparse' | ||
}: WorldOptions) { | ||
@@ -138,7 +72,7 @@ if (maxEntities > MAX_NUM_ENTITIES) { | ||
this.maxEntities = maxEntities; | ||
this.defaultComponentStorage = defaultComponentStorage; | ||
this.shapeLog = new Log(maxShapeChangesPerFrame, 'maxShapeChangesPerFrame'); | ||
this.shapeLogFramePointer = this.shapeLog.createPointer(); | ||
this.indexer = new Indexer(maxRefs); | ||
this.registry = | ||
new Registry(maxEntities, maxLimboEntities, componentTypes.flat(Infinity), this); | ||
this.registry = new Registry( | ||
maxEntities, maxLimboEntities, maxLimboComponents, componentTypes.flat(Infinity), this); | ||
this.systems = this.normalizeAndInitSystems(systemTypes); | ||
@@ -145,0 +79,0 @@ if (this.systems.some(system => system.hasWriteQueries)) { |
@@ -37,2 +37,3 @@ import {ComponentType, initComponent} from './component'; | ||
this.__registry.setFlag(this.__id, type); | ||
STATS: this.__registry.dispatcher.stats.for(type).numEntities += 1; | ||
initComponent(type, this.__id, values); | ||
@@ -91,3 +92,3 @@ return this; | ||
} | ||
if (type.__trackedWrites) this.__registry.trackWrite(this.__id, type); | ||
if (type.__binding!.trackedWrites) this.__registry.trackWrite(this.__id, type); | ||
return type.__bind!(this.__id, true); | ||
@@ -108,7 +109,9 @@ } | ||
this.__deindexOutboundRefs(type); | ||
if (type.__delete) this.__registry.queueRemoval(this.__id, type); | ||
this.__registry.clearFlag(this.__id, type); | ||
STATS: this.__registry.dispatcher.stats.for(type).numEntities -= 1; | ||
} | ||
private __deindexOutboundRefs(type: ComponentType<any>): void { | ||
const fields = type.__fields!; | ||
const fields = type.__binding!.fields; | ||
if (fields.some(field => field.type === Type.ref)) { | ||
@@ -115,0 +118,0 @@ const component = this.write(type); |
@@ -103,3 +103,2 @@ import {Bitset} from './datastructures'; | ||
this.currentEntities?.unset(id); | ||
this.system.removedEntities.set(id); | ||
this.results.all?.remove(id); | ||
@@ -213,3 +212,3 @@ this.results.removed?.add(id); | ||
this.set('__trackMask'); | ||
for (const type of this.__lastTypes) type.__trackedWrites = true; | ||
for (const type of this.__lastTypes) type.__binding!.trackedWrites = true; | ||
return this; | ||
@@ -216,0 +215,0 @@ } |
@@ -1,5 +0,5 @@ | ||
import {ComponentType, decorateComponentType} from './component'; | ||
import {ComponentType, assimilateComponentType} from './component'; | ||
import {Log, LogPointer, Uint32Pool, UnsharedPool} from './datastructures'; | ||
import type {Dispatcher} from './dispatcher'; | ||
import {Entity, EntityId, ENTITY_ID_BITS} from './entity'; | ||
import {Entity, EntityId, ENTITY_ID_BITS, ENTITY_ID_MASK} from './entity'; | ||
import type {SystemBox} from './system'; | ||
@@ -57,2 +57,3 @@ | ||
private readonly shapes: Uint32Array; | ||
private readonly staleShapes: Uint32Array; | ||
private readonly entityIdPool: Uint32Pool; | ||
@@ -64,12 +65,16 @@ readonly pool: EntityPool; | ||
private readonly oldDeletionPointer: LogPointer; | ||
private readonly removalLog: Log; | ||
private readonly prevRemovalPointer: LogPointer; | ||
private readonly oldRemovalPointer: LogPointer; | ||
constructor( | ||
maxEntities: number, maxLimboEntities: number, | ||
maxEntities: number, maxLimboEntities: number, maxLimboComponents: number, | ||
readonly types: ComponentType<any>[], readonly dispatcher: Dispatcher | ||
) { | ||
let componentId = 0; | ||
for (const type of types) decorateComponentType(componentId++, type, this.dispatcher); | ||
for (const type of types) assimilateComponentType(componentId++, type, this.dispatcher); | ||
this.stride = Math.ceil(types.length / 32); | ||
const size = maxEntities * this.stride * 4; | ||
this.shapes = new Uint32Array(new SharedArrayBuffer(size)); | ||
this.staleShapes = new Uint32Array(new SharedArrayBuffer(size)); | ||
this.entityIdPool = new UnsharedPool(maxEntities, 'maxEntities'); | ||
@@ -81,2 +86,5 @@ this.entityIdPool.fillWithDescendingIntegers(0); | ||
this.oldDeletionPointer = this.deletionLog.createPointer(); | ||
this.removalLog = new Log(maxLimboComponents, 'maxLimboComponents'); | ||
this.prevRemovalPointer = this.removalLog.createPointer(); | ||
this.oldRemovalPointer = this.removalLog.createPointer(); | ||
} | ||
@@ -98,8 +106,18 @@ | ||
queueRemoval(id: EntityId, type: ComponentType<any>): void { | ||
this.removalLog.push(id | (type.id! << ENTITY_ID_BITS)); | ||
} | ||
flush(): void { | ||
this.pool.returnTemporaryBorrows(); | ||
this.deletionLog.commit(); | ||
this.removalLog.commit(); | ||
} | ||
processEndOfFrame(): void { | ||
this.processDeletionLog(); | ||
this.processRemovalLog(); | ||
} | ||
private processDeletionLog(): void { | ||
this.deletionLog.commit(); | ||
@@ -123,4 +141,32 @@ let numDeletedEntities = 0; | ||
private processRemovalLog(): void { | ||
this.removalLog.commit(); | ||
let numRemovedComponents = 0; | ||
let log: Uint32Array | undefined, startIndex: number | undefined, endIndex: number | undefined; | ||
while (true) { | ||
[log, startIndex, endIndex] = | ||
this.removalLog.processSince(this.oldRemovalPointer, this.prevRemovalPointer); | ||
if (!log) break; | ||
for (let i = startIndex!; i < endIndex!; i++) { | ||
const entry = log[i]; | ||
const entityId = entry & ENTITY_ID_MASK; | ||
const componentId = entry >>> ENTITY_ID_BITS; | ||
const type = this.types[componentId]; | ||
const shapeIndex = entityId * this.stride + type.__binding!.flagOffset; | ||
const mask = type.__binding!.flagMask; | ||
if ((this.shapes[shapeIndex] & mask) === 0) { | ||
this.staleShapes[shapeIndex] &= ~mask; | ||
type.__delete!(entityId); | ||
} | ||
} | ||
numRemovedComponents += endIndex! - startIndex!; | ||
} | ||
STATS: { | ||
this.dispatcher.stats.maxLimboComponents = numRemovedComponents; | ||
} | ||
this.removalLog.createPointer(this.prevRemovalPointer); | ||
} | ||
extendMaskAndSetFlag(mask: number[], type: ComponentType<any>): void { | ||
const flagOffset = type.__flagOffset!; | ||
const flagOffset = type.__binding!.flagOffset!; | ||
if (flagOffset >= mask.length) { | ||
@@ -130,16 +176,14 @@ mask.length = flagOffset + 1; | ||
} | ||
mask[flagOffset] |= type.__flagMask!; | ||
mask[flagOffset] |= type.__binding!.flagMask!; | ||
} | ||
maskHasFlag(mask: number[] | undefined, type: ComponentType<any>): boolean { | ||
return ((mask?.[type.__flagOffset!] ?? 0) & type.__flagMask!) !== 0; | ||
return ((mask?.[type.__binding!.flagOffset] ?? 0) & type.__binding!.flagMask) !== 0; | ||
} | ||
hasFlag(id: EntityId, type: ComponentType<any>, allowRemoved = false): boolean { | ||
const index = id * this.stride + type.__flagOffset!; | ||
if ((this.shapes[index] & type.__flagMask!) !== 0) return true; | ||
if (allowRemoved && this.executingSystem?.removedEntities.get(id) && | ||
((this.executingSystem.rwMasks.read?.[type.__flagOffset!] ?? 0) & type.__flagMask!) !== 0) { | ||
return true; | ||
} | ||
const shapeIndex = id * this.stride + type.__binding!.flagOffset; | ||
const mask = type.__binding!.flagMask; | ||
if ((this.shapes[shapeIndex] & mask) !== 0) return true; | ||
if (allowRemoved && (this.staleShapes[shapeIndex] & mask) !== 0) return true; | ||
return false; | ||
@@ -149,3 +193,6 @@ } | ||
setFlag(id: EntityId, type: ComponentType<any>): void { | ||
this.shapes[id * this.stride + type.__flagOffset!] |= type.__flagMask!; | ||
const shapeIndex = id * this.stride + type.__binding!.flagOffset; | ||
const mask = type.__binding!.flagMask; | ||
this.shapes[shapeIndex] |= mask; | ||
this.staleShapes[shapeIndex] |= mask; | ||
this.dispatcher.shapeLog.push(id); | ||
@@ -155,3 +202,3 @@ } | ||
clearFlag(id: EntityId, type: ComponentType<any>): void { | ||
this.shapes[id * this.stride + type.__flagOffset!] &= ~type.__flagMask!; | ||
this.shapes[id * this.stride + type.__binding!.flagOffset] &= ~type.__binding!.flagMask; | ||
this.dispatcher.shapeLog.push(id); | ||
@@ -161,3 +208,3 @@ } | ||
trackWrite(id: EntityId, type: ComponentType<any>): void { | ||
this.dispatcher.writeLog!.push(id | (type.__id! << ENTITY_ID_BITS)); | ||
this.dispatcher.writeLog!.push(id | (type.id! << ENTITY_ID_BITS)); | ||
} | ||
@@ -164,0 +211,0 @@ |
@@ -45,3 +45,2 @@ import type {ComponentType} from './component'; | ||
private processedEntities: Bitset; | ||
removedEntities: Bitset; | ||
private shapeLogPointer: LogPointer; | ||
@@ -57,3 +56,2 @@ private writeLogPointer: LogPointer | undefined; | ||
this.processedEntities = new Bitset(dispatcher.maxEntities); | ||
this.removedEntities = new Bitset(dispatcher.maxEntities); | ||
for (const builder of system.__queryBuilders!) builder.__build(this); | ||
@@ -60,0 +58,0 @@ system.__queryBuilders = null; |
476
src/type.ts
@@ -1,3 +0,3 @@ | ||
import type {Binding} from './component'; | ||
import type {Entity} from './entity'; | ||
import type {Binding, ComponentType, Field} from './component'; | ||
import type {Entity, EntityId} from './entity'; | ||
@@ -17,5 +17,4 @@ const encoder = new TextEncoder(); | ||
abstract define<C>( | ||
binding: Binding<C>, name: string, buffer?: SharedArrayBuffer | ||
): SharedArrayBuffer; | ||
abstract defineElastic(binding: Binding<any>, field: Field<any>): void; | ||
abstract defineFixed(binding: Binding<any>, field: Field<any>): void; | ||
@@ -34,2 +33,4 @@ static boolean: Type<boolean>; | ||
static ref: Type<Entity | null>; | ||
static object: Type<any>; | ||
static weakObject: Type<any>; | ||
} | ||
@@ -40,8 +41,43 @@ | ||
define<C>( | ||
binding: Binding<C>, name: string, buffer?: SharedArrayBuffer | ||
): SharedArrayBuffer { | ||
if (!buffer) buffer = new SharedArrayBuffer(binding.maxEntities); | ||
defineElastic<C>(binding: Binding<C>, field: Field<boolean>): void { | ||
let buffer: SharedArrayBuffer; | ||
let data: Uint8Array; | ||
field.updateBuffer = () => { | ||
const capacityChanged = field.buffer?.byteLength !== binding.capacity; | ||
if (!capacityChanged && field.buffer === buffer) return; | ||
buffer = capacityChanged ? new SharedArrayBuffer(binding.capacity) : field.buffer!; | ||
data = new Uint8Array(buffer); | ||
if (capacityChanged && field.buffer) data.set(new Uint8Array(field.buffer)); | ||
field.buffer = buffer; | ||
}; | ||
field.updateBuffer(); | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): boolean { | ||
return Boolean(data[binding.index]); | ||
}, | ||
set(this: C, value: boolean): void { | ||
data[binding.index] = value ? 1 : 0; | ||
} | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): boolean { | ||
return Boolean(data[binding.index]); | ||
}, | ||
set(this: C, value: boolean): void { | ||
throwNotWritable(binding); | ||
} | ||
}); | ||
} | ||
defineFixed<C>(binding: Binding<C>, field: Field<boolean>): void { | ||
const buffer = new SharedArrayBuffer(binding.capacity); | ||
const data = new Uint8Array(buffer); | ||
Object.defineProperty(binding.writableInstance, name, { | ||
field.buffer = buffer; | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
@@ -55,3 +91,4 @@ get(this: C): boolean { | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, name, { | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
@@ -65,3 +102,2 @@ get(this: C): boolean { | ||
}); | ||
return buffer; | ||
} | ||
@@ -84,10 +120,45 @@ } | ||
define<C>( | ||
binding: Binding<C>, name: string, buffer?: SharedArrayBuffer | ||
): SharedArrayBuffer { | ||
if (!buffer) { | ||
buffer = new SharedArrayBuffer(binding.maxEntities * this.NumberArray.BYTES_PER_ELEMENT); | ||
} | ||
defineElastic<C>(binding: Binding<C>, field: Field<number>): void { | ||
let buffer: SharedArrayBuffer; | ||
let data: TypedNumberArray; | ||
field.updateBuffer = () => { | ||
const size = binding.capacity * this.NumberArray.BYTES_PER_ELEMENT; | ||
const capacityChanged = field.buffer?.byteLength !== size; | ||
if (!capacityChanged && field.buffer === buffer) return; | ||
buffer = capacityChanged ? new SharedArrayBuffer(size) : field.buffer!; | ||
data = new this.NumberArray(buffer); | ||
if (capacityChanged && field.buffer) data.set(new this.NumberArray(field.buffer)); | ||
field.buffer = buffer; | ||
}; | ||
field.updateBuffer(); | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): number { | ||
return data[binding.index]; | ||
}, | ||
set(this: C, value: number): void { | ||
data[binding.index] = value; | ||
} | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): number { | ||
return data[binding.index]; | ||
}, | ||
set(this: C, value: number): void { | ||
throwNotWritable(binding); | ||
} | ||
}); | ||
} | ||
defineFixed<C>(binding: Binding<C>, field: Field<number>): void { | ||
const size = binding.capacity * this.NumberArray.BYTES_PER_ELEMENT; | ||
const buffer = new SharedArrayBuffer(size); | ||
const data = new this.NumberArray(buffer); | ||
Object.defineProperty(binding.writableInstance, name, { | ||
field.buffer = buffer; | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
@@ -101,3 +172,4 @@ get(this: C): number { | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, name, { | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
@@ -111,3 +183,2 @@ get(this: C): number { | ||
}); | ||
return buffer; | ||
} | ||
@@ -129,11 +200,19 @@ } | ||
define<C>( | ||
binding: Binding<C>, name: string, buffer?: SharedArrayBuffer | ||
): SharedArrayBuffer { | ||
if (!buffer) { | ||
buffer = new SharedArrayBuffer(binding.maxEntities * this.TypedArray.BYTES_PER_ELEMENT); | ||
} | ||
const data = new this.TypedArray(buffer); | ||
defineElastic<C>(binding: Binding<C>, field: Field<string>): void { | ||
let buffer: SharedArrayBuffer; | ||
let data: Uint8Array | Uint16Array | Uint32Array; | ||
const choices = this.choices, choicesIndex = this.choicesIndex; | ||
Object.defineProperty(binding.writableInstance, name, { | ||
field.updateBuffer = () => { | ||
const size = binding.capacity * this.TypedArray.BYTES_PER_ELEMENT; | ||
const capacityChanged = field.buffer?.byteLength !== size; | ||
if (!capacityChanged && field.buffer === buffer) return; | ||
buffer = capacityChanged ? new SharedArrayBuffer(size) : field.buffer!; | ||
data = new this.TypedArray(buffer); | ||
if (capacityChanged && field.buffer) data.set(new this.TypedArray(field.buffer)); | ||
field.buffer = buffer; | ||
}; | ||
field.updateBuffer(); | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
@@ -152,3 +231,4 @@ get(this: C): string { | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, name, { | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
@@ -165,4 +245,39 @@ get(this: C): string { | ||
}); | ||
return buffer; | ||
} | ||
defineFixed<C>(binding: Binding<C>, field: Field<string>): void { | ||
const choices = this.choices, choicesIndex = this.choicesIndex; | ||
const size = binding.capacity * this.TypedArray.BYTES_PER_ELEMENT; | ||
const buffer = new SharedArrayBuffer(size); | ||
const data = new this.TypedArray(buffer); | ||
field.buffer = buffer; | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): string { | ||
const index = data[binding.index]; | ||
const result = choices[index]; | ||
if (result === undefined) throw new Error(`Invalid static string index: ${index}`); | ||
return result; | ||
}, | ||
set(this: C, value: string): void { | ||
const index = choicesIndex.get(value); | ||
if (index === undefined) throw new Error(`Static string not in set: "${value}"`); | ||
data[binding.index] = index; | ||
} | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): string { | ||
const index = data[binding.index]; | ||
const result = choices[index]; | ||
if (result === undefined) throw new Error(`Invalid static string index: ${index}`); | ||
return result; | ||
}, | ||
set(this: C, value: string): void { | ||
throwNotWritable(binding); | ||
} | ||
}); | ||
} | ||
} | ||
@@ -182,14 +297,22 @@ | ||
define<C>( | ||
binding: Binding<C>, name: string, buffer?: SharedArrayBuffer | ||
): SharedArrayBuffer { | ||
if (!buffer) { | ||
const bufferSize = binding.maxEntities * (this.maxUtf8Length + Uint16Array.BYTES_PER_ELEMENT); | ||
buffer = new SharedArrayBuffer(bufferSize); | ||
} | ||
const lengths = new Uint16Array(buffer); | ||
const bytes = new Uint8Array(buffer); | ||
defineElastic<C>(binding: Binding<C>, field: Field<string>): void { | ||
let buffer: SharedArrayBuffer; | ||
let lengths: Uint16Array; | ||
let bytes: Uint8Array; | ||
const maxUtf8Length = this.maxUtf8Length; | ||
const lengthsStride = this.lengthsStride, bytesStride = this.bytesStride; | ||
Object.defineProperty(binding.writableInstance, name, { | ||
field.updateBuffer = () => { | ||
const size = binding.capacity * (this.maxUtf8Length + Uint16Array.BYTES_PER_ELEMENT); | ||
const capacityChanged = field.buffer?.byteLength !== size; | ||
if (!capacityChanged && field.buffer === buffer) return; | ||
buffer = capacityChanged ? new SharedArrayBuffer(size) : field.buffer!; | ||
lengths = new Uint16Array(buffer); | ||
bytes = new Uint8Array(buffer); | ||
if (capacityChanged && field.buffer) bytes.set(new Uint8Array(field.buffer)); | ||
field.buffer = buffer; | ||
}; | ||
field.updateBuffer(); | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
@@ -210,3 +333,4 @@ get(this: C): string { | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, name, { | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
@@ -222,4 +346,42 @@ get(this: C): string { | ||
}); | ||
return buffer; | ||
} | ||
defineFixed<C>(binding: Binding<C>, field: Field<string>): void { | ||
const maxUtf8Length = this.maxUtf8Length; | ||
const lengthsStride = this.lengthsStride, bytesStride = this.bytesStride; | ||
const size = binding.capacity * (this.maxUtf8Length + Uint16Array.BYTES_PER_ELEMENT); | ||
const buffer = new SharedArrayBuffer(size); | ||
const lengths = new Uint16Array(buffer); | ||
const bytes = new Uint8Array(buffer); | ||
field.buffer = buffer; | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): string { | ||
const length = lengths[binding.index * lengthsStride]; | ||
return decoder.decode( | ||
new Uint8Array(bytes.buffer, binding.index * bytesStride + 2, length)); | ||
}, | ||
set(this: C, value: string): void { | ||
const encodedString = encoder.encode(value); | ||
if (encodedString.byteLength > maxUtf8Length) { | ||
throw new Error(`Dynamic string length > ${maxUtf8Length} after encoding: ${value}`); | ||
} | ||
lengths[binding.index * lengthsStride] = encodedString.byteLength; | ||
bytes.set(encodedString, binding.index * bytesStride + 2); | ||
} | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): string { | ||
const length = lengths[binding.index * lengthsStride]; | ||
return decoder.decode( | ||
new Uint8Array(bytes.buffer, binding.index * bytesStride + 2, length)); | ||
}, | ||
set(this: C, value: string): void { | ||
throwNotWritable(binding); | ||
} | ||
}); | ||
} | ||
} | ||
@@ -232,8 +394,55 @@ | ||
define<C>( | ||
binding: Binding<C>, name: string, buffer?: SharedArrayBuffer | ||
): SharedArrayBuffer { | ||
if (!buffer) buffer = new SharedArrayBuffer(binding.maxEntities * Int32Array.BYTES_PER_ELEMENT); | ||
defineElastic<C>(binding: Binding<C>, field: Field<Entity | null>): void { | ||
let buffer: SharedArrayBuffer; | ||
let data: Int32Array; | ||
field.updateBuffer = () => { | ||
const size = binding.capacity * Int32Array.BYTES_PER_ELEMENT; | ||
const capacityChanged = field.buffer?.byteLength !== size; | ||
if (!capacityChanged && field.buffer === buffer) return; | ||
buffer = capacityChanged ? new SharedArrayBuffer(size) : field.buffer!; | ||
data = new Int32Array(buffer); | ||
if (capacityChanged && field.buffer) data.set(new Int32Array(field.buffer)); | ||
field.buffer = buffer; | ||
}; | ||
field.updateBuffer(); | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): Entity | null { | ||
const id = data[binding.index]; | ||
if (id === -1) return null; | ||
return binding.dispatcher.registry.pool.borrowTemporarily(id); | ||
}, | ||
set(this: C, value: Entity | null): void { | ||
const oldId = data[binding.index]; | ||
const newId = value?.__id ?? -1; | ||
if (oldId === newId) return; | ||
// TODO: deindex/reindex ref | ||
// if (oldId !== 0) indexer.remove(oldId, binding.entityId); | ||
data[binding.index] = newId; | ||
// if (newId !== 0) indexer.insert(newId, binding.entityId); | ||
} | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): Entity | null { | ||
const id = data[binding.index]; | ||
if (id === -1) return null; | ||
return binding.dispatcher.registry.pool.borrowTemporarily(id); | ||
}, | ||
set(this: C, value: Entity | null): void { | ||
throwNotWritable(binding); | ||
} | ||
}); | ||
} | ||
defineFixed<C>(binding: Binding<C>, field: Field<Entity | null>): void { | ||
const size = binding.capacity * Int32Array.BYTES_PER_ELEMENT; | ||
const buffer = new SharedArrayBuffer(size); | ||
const data = new Int32Array(buffer); | ||
Object.defineProperty(binding.writableInstance, name, { | ||
field.buffer = buffer; | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
@@ -249,9 +458,10 @@ get(this: C): Entity | null { | ||
if (oldId === newId) return; | ||
const indexer = binding.dispatcher.indexer; | ||
if (oldId !== 0) indexer.remove(oldId, binding.entityId); | ||
// TODO: deindex/reindex ref | ||
// if (oldId !== 0) indexer.remove(oldId, binding.entityId); | ||
data[binding.index] = newId; | ||
if (newId !== 0) indexer.insert(newId, binding.entityId); | ||
// if (newId !== 0) indexer.insert(newId, binding.entityId); | ||
} | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, name, { | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
@@ -267,6 +477,164 @@ get(this: C): Entity | null { | ||
}); | ||
return buffer; | ||
} | ||
} | ||
class ObjectType extends Type<any> { | ||
constructor() {super(undefined);} | ||
defineElastic<C>(binding: Binding<C>, field: Field<any>): void { | ||
const data: any[] = []; | ||
field.localBuffer = data; | ||
field.updateBuffer = () => {/* no-op */}; | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): any { | ||
return data[binding.index]; | ||
}, | ||
set(this: C, value: any): void { | ||
data[binding.index] = value; | ||
} | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): any { | ||
return data[binding.index]; | ||
}, | ||
set(this: C, value: any): void { | ||
throwNotWritable(binding); | ||
} | ||
}); | ||
} | ||
defineFixed<C>(binding: Binding<C>, field: Field<boolean>): void { | ||
const data: any[] = new Array(binding.capacity); | ||
field.localBuffer = data; | ||
field.updateBuffer = () => {/* no-op */}; | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): any { | ||
return data[binding.index]; | ||
}, | ||
set(this: C, value: any): void { | ||
data[binding.index] = value; | ||
} | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): any { | ||
return data[binding.index]; | ||
}, | ||
set(this: C, value: any): void { | ||
throwNotWritable(binding); | ||
} | ||
}); | ||
} | ||
} | ||
type FinalizerHeldValue = { | ||
type: ComponentType<any>, field: Field<any>, weakRef: WeakRef<any>, id: EntityId, index: number | ||
}; | ||
class WeakObjectType extends Type<any> { | ||
private finalizers: FinalizationRegistry | undefined; | ||
constructor() {super(undefined);} | ||
defineElastic<C>(binding: Binding<C>, field: Field<any>): void { | ||
const data: WeakRef<any>[] = []; | ||
field.localBuffer = data; | ||
field.updateBuffer = () => {/* no-op */}; | ||
const finalizers = this.initFinalizers(binding); | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): any { | ||
const value = data[binding.index]; | ||
if (value === null || value === undefined) return value; | ||
return value.deref(); | ||
}, | ||
set(this: C, value: any): void { | ||
if (value !== null && value !== undefined) { | ||
const weakRef = new WeakRef(value); | ||
finalizers?.register( | ||
value, | ||
{type: binding.type, field, weakRef, id: binding.entityId, index: binding.index} | ||
); | ||
value = weakRef; | ||
} | ||
data[binding.index] = value; | ||
} | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): any { | ||
const value = data[binding.index]; | ||
if (value === null || value === undefined) return value; | ||
return value.deref(); | ||
}, | ||
set(this: C, value: any): void { | ||
throwNotWritable(binding); | ||
} | ||
}); | ||
} | ||
defineFixed<C>(binding: Binding<C>, field: Field<boolean>): void { | ||
const data: WeakRef<any>[] = new Array(binding.capacity); | ||
field.localBuffer = data; | ||
field.updateBuffer = () => {/* no-op */}; | ||
const finalizers = this.initFinalizers(binding); | ||
Object.defineProperty(binding.writableInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): any { | ||
const value = data[binding.index]; | ||
if (value === null || value === undefined) return value; | ||
return value.deref(); | ||
}, | ||
set(this: C, value: any): void { | ||
if (value !== null && value !== undefined) { | ||
const weakRef = new WeakRef(value); | ||
finalizers?.register( | ||
value, | ||
{type: binding.type, field, weakRef, id: binding.entityId, index: binding.index} | ||
); | ||
value = weakRef; | ||
} | ||
data[binding.index] = value; | ||
} | ||
}); | ||
Object.defineProperty(binding.readonlyInstance, field.name, { | ||
enumerable: true, configurable: true, | ||
get(this: C): any { | ||
const value = data[binding.index]; | ||
if (value === null || value === undefined) return value; | ||
return value.deref(); | ||
}, | ||
set(this: C, value: any): void { | ||
throwNotWritable(binding); | ||
} | ||
}); | ||
} | ||
private initFinalizers(binding: Binding<any>) { | ||
if (!binding.trackedWrites) return; | ||
if (this.finalizers) return this.finalizers; | ||
const dispatcher = binding.dispatcher; | ||
if (!dispatcher.writeLog || typeof FinalizationRegistry === 'undefined') return; | ||
this.finalizers = new FinalizationRegistry( | ||
({type, field, weakRef, id, index}: FinalizerHeldValue) => { | ||
if (field.localBuffer?.[index] === weakRef) { | ||
dispatcher.registry.trackWrite(id, type); | ||
} | ||
} | ||
); | ||
return this.finalizers; | ||
} | ||
} | ||
Type.boolean = new BooleanType(); | ||
@@ -284,1 +652,3 @@ Type.uint8 = new NumberType(Uint8Array); | ||
Type.ref = new RefType(); | ||
Type.object = new ObjectType(); | ||
Type.weakObject = new WeakObjectType(); |
import type {ComponentType} from './component'; | ||
import {Dispatcher, WorldOptions} from './dispatcher'; | ||
import type {Stats} from './stats'; | ||
import type {System} from './system'; | ||
@@ -25,5 +26,5 @@ | ||
get stats(): any { | ||
get stats(): Stats { | ||
return this.__dispatcher.stats; | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1711542
13709
34
16