UNPKG

@dcl/ecs

Version:
288 lines (287 loc) • 13 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Engine = void 0; const components = __importStar(require("../components")); const component_number_1 = require("../components/component-number"); const invariant_1 = require("../runtime/invariant"); const schemas_1 = require("../schemas"); const crdt_1 = require("../systems/crdt"); const lww_element_set_component_definition_1 = require("./lww-element-set-component-definition"); const entity_1 = require("./entity"); const systems_1 = require("./systems"); const grow_only_value_set_component_definition_1 = require("./grow-only-value-set-component-definition"); const tree_1 = require("../runtime/helpers/tree"); const crdt_2 = require("../serialization/crdt"); __exportStar(require("./input"), exports); __exportStar(require("./readonly"), exports); __exportStar(require("./types"), exports); function preEngine(options) { const entityContainer = options?.entityContainer ?? (0, entity_1.createEntityContainer)(); const componentsDefinition = new Map(); const systems = (0, systems_1.SystemContainer)(); let sealed = false; function addSystem(fn, priority = systems_1.SYSTEMS_REGULAR_PRIORITY, name) { systems.add(fn, priority, name); } function removeSystem(selector) { return systems.remove(selector); } function addEntity() { const entity = entityContainer.generateEntity(); return entity; } function removeEntity(entity) { for (const [, component] of componentsDefinition) { // TODO: hack for the moment. // We still need the NetworkEntity to forward this message to the SyncTransport. // If we remove it then we can't notify the other users which entity was deleted. if (component.componentName === 'core-schema::Network-Entity') continue; component.entityDeleted(entity, true); } return entityContainer.removeEntity(entity); } function removeEntityWithChildren(entity) { return (0, tree_1.removeEntityWithChildren)({ removeEntity, defineComponentFromSchema, getEntitiesWith, defineComponent }, entity); } function registerComponentDefinition(componentName, component) { /* istanbul ignore next */ if (sealed) throw new Error('Engine is already sealed. No components can be added at this stage'); const componentId = (0, component_number_1.componentNumberFromName)(componentName); const prev = componentsDefinition.get(componentId); if (prev) { throw new Error(`Component number ${componentId} was already registered.`); } /* istanbul ignore next */ if (component.componentName !== componentName) { throw new Error(`Component name doesn't match componentDefinition.componentName ${componentName} != ${component.componentName}`); } /* istanbul ignore next */ if (component.componentId !== componentId) { throw new Error(`Component number doesn't match componentDefinition.componentId ${componentId} != ${component.componentId}`); } componentsDefinition.set(componentId, component); return component; } function defineComponentFromSchema(componentName, schema) { const componentId = (0, component_number_1.componentNumberFromName)(componentName); const prev = componentsDefinition.get(componentId); if (prev) { // TODO: assert spec === prev.spec return prev; } /* istanbul ignore next */ if (sealed) throw new Error('Engine is already sealed. No components can be added at this stage'); const newComponent = (0, lww_element_set_component_definition_1.createComponentDefinitionFromSchema)(componentName, componentId, schema); componentsDefinition.set(componentId, newComponent); return newComponent; } function defineValueSetComponentFromSchema(componentName, schema, options) { const componentId = (0, component_number_1.componentNumberFromName)(componentName); const prev = componentsDefinition.get(componentId); if (prev) { // TODO: assert spec === prev.spec return prev; } /* istanbul ignore next */ if (sealed) throw new Error('Engine is already sealed. No components can be added at this stage'); const newComponent = (0, grow_only_value_set_component_definition_1.createValueSetComponentDefinitionFromSchema)(componentName, componentId, schema, options); componentsDefinition.set(componentId, newComponent); return newComponent; } function defineComponent(componentName, mapSpec, constructorDefault) { const componentId = (0, component_number_1.componentNumberFromName)(componentName); const prev = componentsDefinition.get(componentId); if (prev) { // TODO: assert spec === prev.spec return prev; } if (sealed) throw new Error('Engine is already sealed. No components can be added at this stage'); const schemaSpec = schemas_1.Schemas.Map(mapSpec, constructorDefault); const def = (0, lww_element_set_component_definition_1.createComponentDefinitionFromSchema)(componentName, componentId, schemaSpec); const newComponent = { ...def, create(entity, val) { return def.create(entity, val); }, createOrReplace(entity, val) { return def.createOrReplace(entity, val); } }; componentsDefinition.set(componentId, newComponent); return newComponent; } function getComponent(componentIdOrName) { const componentId = typeof componentIdOrName === 'number' ? componentIdOrName : (0, component_number_1.componentNumberFromName)(componentIdOrName); const component = componentsDefinition.get(componentId); if (!component) { throw new Error(`Component ${componentIdOrName} not found. You need to declare the components at the beginnig of the engine declaration`); } return component; } function getComponentOrNull(componentIdOrName) { const componentId = typeof componentIdOrName === 'number' ? componentIdOrName : (0, component_number_1.componentNumberFromName)(componentIdOrName); return (componentsDefinition.get(componentId) ?? /* istanbul ignore next */ null); } function* getEntitiesWith(...components) { for (const [entity, ...groupComp] of getComponentDefGroup(...components)) { yield [entity, ...groupComp.map((c) => c.get(entity))]; } } function getEntityOrNullByName(value) { const NameComponent = components.Name({ defineComponent }); for (const [entity, name] of getEntitiesWith(NameComponent)) { if (name.value === value) return entity; } return null; } function getEntityByName(value) { const entity = getEntityOrNullByName(value); return entity; } function* getComponentDefGroup(...args) { const [firstComponentDef, ...componentDefinitions] = args; for (const [entity] of firstComponentDef.iterator()) { let matches = true; for (const componentDef of componentDefinitions) { if (!componentDef.has(entity)) { matches = false; break; } } if (matches) { yield [entity, ...args]; } } } function getSystems() { return systems.getSystems(); } function componentsIter() { return componentsDefinition.values(); } function removeComponentDefinition(componentIdOrName) { if (sealed) throw new Error('Engine is already sealed. No components can be removed at this stage'); const componentId = typeof componentIdOrName === 'number' ? componentIdOrName : (0, component_number_1.componentNumberFromName)(componentIdOrName); componentsDefinition.delete(componentId); } components.Transform({ defineComponentFromSchema }); function seal() { if (!sealed) { sealed = true; } } return { addEntity, removeEntity, removeEntityWithChildren, addSystem, getSystems, removeSystem, defineComponent, defineComponentFromSchema, defineValueSetComponentFromSchema, getEntitiesWith, getComponent, getComponentOrNull: getComponentOrNull, getEntityOrNullByName, getEntityByName, removeComponentDefinition, registerComponentDefinition, entityContainer, componentsIter, seal }; } /** * Internal constructor of new engines, this is an internal API * @public * @deprecated Prevent manual usage prefer "engine" for scene development */ function Engine(options) { const partialEngine = preEngine(options); const onChangeFunction = (entity, operation, component, componentValue) => { if (operation === crdt_2.CrdtMessageType.DELETE_ENTITY) { for (const component of partialEngine.componentsIter()) { component?.__onChangeCallbacks(entity, undefined); } } else { component?.__onChangeCallbacks(entity, componentValue); } return options?.onChangeFunction(entity, operation, component, componentValue); }; const crdtSystem = (0, crdt_1.crdtSceneSystem)(partialEngine, onChangeFunction); async function update(dt) { await crdtSystem.receiveMessages(); for (const system of partialEngine.getSystems()) { const ret = system.fn(dt); (0, invariant_1.checkNotThenable)(ret, `A system (${system.name || 'anonymous'}) returned a thenable. Systems cannot be async functions. Documentation: https://dcl.gg/sdk/sync-systems`); } // get the deleted entities to send the DeleteEntity CRDT commands const deletedEntites = partialEngine.entityContainer.releaseRemovedEntities(); await crdtSystem.sendMessages(deletedEntites); } return { _id: Date.now(), addEntity: partialEngine.addEntity, removeEntity: partialEngine.removeEntity, removeEntityWithChildren: partialEngine.removeEntityWithChildren, addSystem: partialEngine.addSystem, removeSystem: partialEngine.removeSystem, defineComponent: partialEngine.defineComponent, defineComponentFromSchema: partialEngine.defineComponentFromSchema, defineValueSetComponentFromSchema: partialEngine.defineValueSetComponentFromSchema, registerComponentDefinition: partialEngine.registerComponentDefinition, getEntitiesWith: partialEngine.getEntitiesWith, getComponent: partialEngine.getComponent, getComponentOrNull: partialEngine.getComponentOrNull, removeComponentDefinition: partialEngine.removeComponentDefinition, componentsIter: partialEngine.componentsIter, seal: partialEngine.seal, getEntityOrNullByName: partialEngine.getEntityOrNullByName, getEntityByName: partialEngine.getEntityByName, update, RootEntity: 0, PlayerEntity: 1, CameraEntity: 2, getEntityState: partialEngine.entityContainer.getEntityState, addTransport: crdtSystem.addTransport, entityContainer: partialEngine.entityContainer }; } exports.Engine = Engine;