@dcl/ecs
Version:
Decentraland ECS
288 lines (287 loc) • 13 kB
JavaScript
;
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;