UNPKG

@javelin/ecs

Version:

184 lines 7.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createStorage = void 0; const core_1 = require("@javelin/core"); const archetype_1 = require("./archetype"); const component_1 = require("./component"); const internal_1 = require("./internal"); const signal_1 = require("./signal"); const ERROR_ENTITY_NOT_CREATED = "Failed to locate entity: entity has not been created"; const ERROR_ALREADY_DESTROYED = "Failed to locate entity: entity has been destroyed"; const ERROR_NO_SCHEMA = "Failed to locate component: schema not registered"; function createStorage(options = {}) { const archetypes = options.snapshot ? options.snapshot.archetypes.map(snapshot => archetype_1.createArchetype({ snapshot })) : [archetype_1.createArchetype({ type: [] })]; const entityIndex = []; const entityRelocating = signal_1.createSignal(); const entityRelocated = signal_1.createSignal(); const archetypeCreated = signal_1.createSignal(); function findArchetype(components) { const length = components.length; outer: for (let i = 0; i < archetypes.length; i++) { const archetype = archetypes[i]; const { type, typeInverse } = archetype; if (type.length !== length) { continue; } for (let j = 0; j < length; j++) { if (typeInverse[component_1.getSchemaId(components[j])] === undefined) { continue outer; } } return archetype; } return null; } function findOrCreateArchetype(components) { let archetype = findArchetype(components); if (archetype === null) { archetype = archetype_1.createArchetype({ type: components.map(component_1.getSchemaId), }); archetypes.push(archetype); archetypeCreated.dispatch(archetype); } return archetype; } function getEntityArchetype(entity) { const archetype = entityIndex[entity]; core_1.assert(archetype !== undefined, ERROR_ENTITY_NOT_CREATED); core_1.assert(archetype !== null, ERROR_ALREADY_DESTROYED); return archetype; } function relocate(prev, entity, components, changed) { const next = findOrCreateArchetype(components); entityRelocating.dispatch(entity, prev, next, changed); prev.remove(entity); next.insert(entity, components); entityIndex[entity] = next; entityRelocated.dispatch(entity, prev, next, changed); } function attachComponents(entity, components) { const source = entityIndex[entity]; if (source === undefined || source === null) { const archetype = findOrCreateArchetype(components); entityRelocating.dispatch(entity, archetypes[0], archetype, components); archetype.insert(entity, components); entityIndex[entity] = archetype; entityRelocated.dispatch(entity, archetypes[0], archetype, components); } else { const index = source.indices[entity]; const final = components.slice(); for (let i = 0; i < source.type.length; i++) { const schemaId = source.type[i]; if (components.find(c => component_1.getSchemaId(c) === schemaId)) { // take inserted component continue; } final.push(source.table[i][index]); } relocate(source, entity, final, components); } } function detachBySchemaId(entity, type) { const source = getEntityArchetype(entity); const removed = []; const final = []; const index = source.indices[entity]; for (let i = 0; i < source.type.length; i++) { const schemaId = source.type[i]; const component = source.table[i][index]; (type.includes(schemaId) ? removed : final).push(component); } relocate(source, entity, final, removed); } function clearComponents(entity) { const archetype = getEntityArchetype(entity); detachBySchemaId(entity, archetype.type); entityIndex[entity] = null; } const tmpComponentsToInsert = []; function attachOrUpdateComponents(entity, components) { const archetype = getEntityArchetype(entity); const index = archetype.indices[entity]; core_1.mutableEmpty(tmpComponentsToInsert); for (let i = 0; i < components.length; i++) { const component = components[i]; const column = archetype.typeInverse[component_1.getSchemaId(component)]; if (column === undefined) { // Entity component makeup does not match patch component, insert the new // component. tmpComponentsToInsert.push(component); } else { // Apply patch to component. Object.assign(archetype.table[column][index], component); } } if (tmpComponentsToInsert.length > 0) { attachComponents(entity, tmpComponentsToInsert); } } function hasComponentOfSchema(entity, schema) { const archetype = getEntityArchetype(entity); const type = internal_1.UNSAFE_internals.schemaIndex.get(schema); core_1.assert(type !== undefined, ERROR_NO_SCHEMA); return archetype.type.includes(type); } function getComponentBySchema(entity, schema) { const type = internal_1.UNSAFE_internals.schemaIndex.get(schema); core_1.assert(type !== undefined, ERROR_NO_SCHEMA); return getComponentBySchemaId(entity, type); } function getComponentBySchemaId(entity, schemaId) { const archetype = getEntityArchetype(entity); const column = archetype.typeInverse[schemaId]; if (column === undefined) { return null; } const entityIndex = archetype.indices[entity]; return archetype.table[column][entityIndex]; } function getAllComponents(entity) { const archetype = getEntityArchetype(entity); const entityIndex = archetype.indices[entity]; const result = []; for (let i = 0; i < archetype.table.length; i++) { result.push(archetype.table[i][entityIndex]); } return result; } function clear() { core_1.mutableEmpty(archetypes); core_1.mutableEmpty(entityIndex); } function createSnapshot() { return { archetypes: archetypes.map(archetype => ({ type: archetype.type.slice(), table: archetype.table.map(column => column.map(component => ({ ...component }))), indices: core_1.packSparseArray(archetype.indices), })), }; } return { archetypeCreated, archetypes, attachComponents, attachOrUpdateComponents, clear, clearComponents, detachBySchemaId, entityRelocated, entityRelocating, getComponentBySchemaId, getComponentBySchema, getAllComponents, createSnapshot, hasComponentOfSchema, }; } exports.createStorage = createStorage; //# sourceMappingURL=storage.js.map