@javelin/ecs
Version:
151 lines • 4.64 kB
JavaScript
import { createModel, createSchemaInstance, createStackPool, resetSchemaInstance, } from "@javelin/core";
import { UNSAFE_internals, UNSAFE_setModel } from "./internal";
export const $type = Symbol("javelin_component_type");
export const $pool = Symbol("javelin_component_pool");
const { schemaIndex, schemaPools, instanceTypeLookup } = UNSAFE_internals;
let schemaIds = 0;
function createComponentBase(schema, pool = true) {
return Object.defineProperties({}, {
[$type]: {
value: schemaIndex.get(schema),
writable: false,
enumerable: false,
},
[$pool]: {
value: pool,
writable: false,
enumerable: false,
},
});
}
/**
* Check if a component is an instance of a component type.
* @param component
* @param schema
* @returns
* @example
* const A = {}
* const B = {}
* const a = component(A)
* isComponentOf(a, A) // true
* isComponentOf(a, B) // false
*/
export function isComponentOf(component, schema) {
return getSchemaId(component) === schemaIndex.get(schema);
}
export function createComponentPool(schema, poolSize) {
const componentPool = createStackPool(() => createSchemaInstance(schema, createComponentBase(schema)), component => resetSchemaInstance(component, schema), poolSize);
return componentPool;
}
const modelConfig = new Map();
/**
* Manually register a component type. Optionally specify a unique, integer id
* and/or size for the component type's object pool.
* @param schema
* @param schemaId
* @param [poolSize=1000]
* @returns
* @example <caption>register a schema as a component type (optional)</caption>
* ```ts
* const Vehicle = { torque: number }
* registerSchema(Vehicle)
* ```
* @example <caption>register a schema with a fixed id</caption>
* ```ts
* const Vehicle = { torque: number }
* registerSchema(Vehicle, 22)
* ```
* @example <caption>register a schema with a fixed id and pool size</caption>
* ```ts
* const Particle = { color: number }
* registerSchema(Particle, 3, 10_000)
* ```
*/
export function registerSchema(schema, schemaId, poolSize = 1000) {
let type = schemaIndex.get(schema);
if (type !== undefined) {
return type;
}
type = schemaId;
if (type === undefined) {
while (modelConfig.has(schemaIds)) {
schemaIds++;
}
type = schemaIds;
}
else if (modelConfig.has(type)) {
throw new Error("Failed to register component type: a component with same id is already registered");
}
if (poolSize > 0) {
schemaPools.set(type, createComponentPool(schema, poolSize));
}
modelConfig.set(type, schema);
schemaIndex.set(schema, type);
UNSAFE_setModel(createModel(modelConfig));
return type;
}
function createComponentInner(schema) {
const type = registerSchema(schema);
const pool = UNSAFE_internals.schemaPools.get(type);
return (pool
? pool.retain()
: createSchemaInstance(schema, createComponentBase(schema, false)));
}
/**
* Use a Schema to create a component. The second parameter is an optional
* object that will be used to assign initial values to the new component
* instance.
* @param schema
* @param props
* @returns
* @example
* ```ts
* const Quaternion = { x: number, y: number, z: number, w: number }
* const quaternion = component(Quaternion, { w: 1 })
* ```
*/
export function component(schema, props) {
const instance = createComponentInner(schema);
if (props !== undefined) {
Object.assign(instance, props);
}
return instance;
}
export function toComponentFromType(object, type) {
try {
;
object[$type] = type;
object[$pool] = false;
}
catch { }
if (object[$type] !== type) {
instanceTypeLookup.set(object, type);
}
return object;
}
/**
* Instruct the ECS to treat an object as a component instance of a given
* schema.
* @param object
* @param schema
* @returns
*/
export function toComponent(object, schema) {
const type = registerSchema(schema, undefined, 0);
return toComponentFromType(object, type);
}
/**
* Get the type id (number) of a component. Throws an error if the object is
* not a valid component.
* @param component
* @returns
*/
export function getSchemaId(component) {
var _a;
const type = (_a = component[$type]) !== null && _a !== void 0 ? _a : instanceTypeLookup.get(component);
if (type === undefined) {
throw new Error("Failed to get component type id: object is not a component");
}
return type;
}
//# sourceMappingURL=component.js.map