UNPKG

@javelin/ecs

Version:

237 lines 7.86 kB
import { assert, mutableEmpty } from "@javelin/core"; import { $pool, getSchemaId, registerSchema, } from "./component"; import { UNSAFE_internals } from "./internal"; import { createStorage } from "./storage"; const $systemId = Symbol("javelin_system_id"); export var DeferredOpType; (function (DeferredOpType) { DeferredOpType[DeferredOpType["Create"] = 0] = "Create"; DeferredOpType[DeferredOpType["Attach"] = 1] = "Attach"; DeferredOpType[DeferredOpType["Detach"] = 2] = "Detach"; DeferredOpType[DeferredOpType["Destroy"] = 3] = "Destroy"; })(DeferredOpType || (DeferredOpType = {})); /** * Create a world. * @param options WorldOptions * @returns World */ export function createWorld(options = {}) { var _a, _b; const { topics = [] } = options; const systems = []; const deferredOps = []; const destroyed = new Set(); const storage = createStorage({ snapshot: (_a = options.snapshot) === null || _a === void 0 ? void 0 : _a.storage }); let entityIds = 0; let systemIds = 0; (_b = options.systems) === null || _b === void 0 ? void 0 : _b.forEach(addSystem); function createDeferredOp(...args) { const deferred = []; for (let i = 0; i < args.length; i++) { deferred[i] = args[i]; } return deferred; } function maybeReleaseComponent(component) { const pool = UNSAFE_internals.schemaPools.get(getSchemaId(component)); if (pool && Reflect.get(component, $pool)) { pool.release(component); } } function addSystem(system) { systems.push(system); system[$systemId] = systemIds++; } function removeSystem(system) { const index = systems.indexOf(system); if (index > -1) { systems.splice(index, 1); } } function addTopic(topic) { topics.push(topic); } function removeTopic(topic) { const index = topics.indexOf(topic); if (index > -1) { topics.splice(index, 1); } } function create(...components) { const entity = entityIds++; if (components.length > 0) { deferredOps.push(createDeferredOp(DeferredOpType.Attach, entity, components)); } return entity; } function attach(entity, ...components) { deferredOps.push(createDeferredOp(DeferredOpType.Attach, entity, components)); } function attachImmediate(entity, components) { storage.attachComponents(entity, components); } function detach(entity, ...components) { if (components.length === 0) { return; } const schemaIds = components.map(c => { var _a; return typeof c === "number" ? c : (_a = UNSAFE_internals.schemaIndex.get(c)) !== null && _a !== void 0 ? _a : getSchemaId(c); }); deferredOps.push(createDeferredOp(DeferredOpType.Detach, entity, schemaIds)); } function detachImmediate(entity, schemaIds) { const components = []; for (let i = 0; i < schemaIds.length; i++) { const schemaId = schemaIds[i]; const component = storage.getComponentBySchemaId(entity, schemaId); assert(component !== null, `Failed to detach component: entity does not have component of type ${schemaId}`); components.push(component); } storage.detachBySchemaId(entity, schemaIds); components.forEach(maybeReleaseComponent); } function destroy(entity) { if (destroyed.has(entity)) { return; } deferredOps.push(createDeferredOp(DeferredOpType.Destroy, entity)); destroyed.add(entity); } function destroyImmediate(entity) { storage.clearComponents(entity); } function has(entity, schema) { registerSchema(schema); return storage.hasComponentOfSchema(entity, schema); } function get(entity, schema) { registerSchema(schema); const component = storage.getComponentBySchema(entity, schema); if (component === null) { throw new Error("Failed to get component: entity does not have component"); } return component; } function tryGet(entity, schema) { try { registerSchema(schema); return storage.getComponentBySchema(entity, schema); } catch (error) { return null; } } function reset() { destroyed.clear(); // clear deferred ops mutableEmpty(deferredOps); // remove all systems mutableEmpty(systems); // remove all topics topics.forEach(topic => topic.clear()); mutableEmpty(topics); // reset entity id counter entityIds = 0; // reset step data world.latestTick = -1; world.latestTickData = null; world.latestSystemId = -1; // release components for (let i = 0; i < storage.archetypes.length; i++) { const archetype = storage.archetypes[i]; for (let j = 0; j < archetype.type.length; j++) { const column = archetype.table[j]; const componentPool = UNSAFE_internals.schemaPools.get(archetype.type[j]); for (let k = 0; k < column.length; k++) { const component = column[k]; componentPool === null || componentPool === void 0 ? void 0 : componentPool.release(component); } } } // reset entity-component storage storage.clear(); } function createSnapshot() { return { storage: storage.createSnapshot(), }; } function applyAttachOp(op) { const [, entity, components] = op; attachImmediate(entity, components); } function applyDetachOp(op) { const [, entity, schemaIds] = op; detachImmediate(entity, schemaIds); } function applyDestroyOp(op) { const [, entity] = op; destroyImmediate(entity); } function applyDeferredOp(deferred) { switch (deferred[0]) { case DeferredOpType.Attach: applyAttachOp(deferred); break; case DeferredOpType.Detach: applyDetachOp(deferred); break; case DeferredOpType.Destroy: applyDestroyOp(deferred); break; } } function step(data) { let prevWorld = UNSAFE_internals.currentWorldId; UNSAFE_internals.currentWorldId = id; world.latestTickData = data; for (let i = 0; i < deferredOps.length; i++) { applyDeferredOp(deferredOps[i]); } mutableEmpty(deferredOps); // flush topics for (let i = 0; i < topics.length; i++) { topics[i].flush(); } // execute systems for (let i = 0; i < systems.length; i++) { const system = systems[i]; world.latestSystemId = system[$systemId]; system(world); } destroyed.clear(); world.latestTick++; UNSAFE_internals.currentWorldId = prevWorld; } const id = UNSAFE_internals.worldIds++; const world = { id, storage, latestTick: -1, latestTickData: null, latestSystemId: -1, attach, attachImmediate, addSystem, addTopic, create, destroy, destroyImmediate, get, createSnapshot, has, detach, detachImmediate, removeSystem, removeTopic, reset, step, tryGet, }; UNSAFE_internals.worlds.push(world); return world; } //# sourceMappingURL=world.js.map