@dcl/ecs
Version:
Decentraland ECS
202 lines (201 loc) • 9.87 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;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.instanceComposite = exports.getEntityMapping = exports.getComponentDefinition = exports.getComponentValue = exports.EntityMappingMode = void 0;
const components_1 = require("../components");
const component_number_1 = require("../components/component-number");
const schemas_1 = require("../schemas");
const ByteBuffer_1 = require("../serialization/ByteBuffer");
const components_2 = require("./components");
const path = __importStar(require("./path"));
/** @public */
/* @__PURE__ */
var EntityMappingMode;
(function (EntityMappingMode) {
EntityMappingMode[EntityMappingMode["EMM_NONE"] = 0] = "EMM_NONE";
EntityMappingMode[EntityMappingMode["EMM_NEXT_AVAILABLE"] = 1] = "EMM_NEXT_AVAILABLE";
EntityMappingMode[EntityMappingMode["EMM_DIRECT_MAPPING"] = 2] = "EMM_DIRECT_MAPPING";
})(EntityMappingMode = exports.EntityMappingMode || (exports.EntityMappingMode = {}));
/**
* Return the component value from composite data
* @internal
*/
function getComponentValue(componentDefinition, component) {
if (component.data?.$case === 'json') {
return component.data.json;
}
else {
return componentDefinition.schema.deserialize(new ByteBuffer_1.ReadWriteByteBuffer(component.data?.binary));
}
}
exports.getComponentValue = getComponentValue;
/**
* Return the component definition from composite info
* @internal
*/
function getComponentDefinition(engine, component) {
const existingComponentDefinition = engine.getComponentOrNull(component.name);
if (!existingComponentDefinition) {
if (component.name.startsWith('core::')) {
if (component.name in components_1.componentDefinitionByName) {
return components_1.componentDefinitionByName[component.name](engine);
}
else {
throw new Error(`The core component ${component.name} was not found.`);
}
}
else if (component.jsonSchema) {
return engine.defineComponentFromSchema(component.name, schemas_1.Schemas.fromJson(component.jsonSchema));
}
else {
throw new Error(`${component.name} is not defined and there is no schema to define it.`);
}
}
else {
return existingComponentDefinition;
}
}
exports.getComponentDefinition = getComponentDefinition;
/**
* Return the entity mapping or fail if there is no more
* @internal
*/
function getEntityMapping(engine, compositeEntity, mappedEntities, { entityMapping }) {
const existingEntity = mappedEntities.get(compositeEntity);
if (existingEntity) {
return existingEntity;
}
if (entityMapping?.type === EntityMappingMode.EMM_DIRECT_MAPPING) {
const entity = entityMapping.getCompositeEntity(compositeEntity);
mappedEntities.set(compositeEntity, entity);
return entity;
}
// This function in runtime can be just `engine.addEntity()`
const newEntity = entityMapping?.type === EntityMappingMode.EMM_NEXT_AVAILABLE
? entityMapping.getNextAvailableEntity()
: engine.addEntity();
if (newEntity === null) {
throw new Error('There is no more entities to allocate');
}
mappedEntities.set(compositeEntity, newEntity);
return newEntity;
}
exports.getEntityMapping = getEntityMapping;
/**
* @internal
*/
/* @__PURE__ */
function instanceComposite(engine, compositeResource, compositeProvider, options) {
const { rootEntity, alreadyRequestedSrc: optionalAlreadyRequestedSrc, entityMapping } = options;
const alreadyRequestedSrc = optionalAlreadyRequestedSrc || new Set();
const compositeDirectoryPath = path.dirname(path.resolve(compositeResource.src));
const TransformComponentNumber = (0, component_number_1.componentNumberFromName)('core::Transform');
const CompositeRootComponent = (0, components_2.getCompositeRootComponent)(engine);
// Key => EntityNumber from the composite
// Value => EntityNumber in current engine
const mappedEntities = new Map();
const getCompositeEntity = (compositeEntity) => getEntityMapping(engine, compositeEntity, mappedEntities, options);
// ## 1 ##
// First entity that I want to map, the root entity from the composite to the target entity in the engine
// If there is no `rootEntity` passed, we assign one from `getNextAvailableEntity`
const compositeRootEntity = rootEntity ?? getCompositeEntity(0);
if (rootEntity) {
mappedEntities.set(0, rootEntity);
}
// ## 2 ##
// If there are more composite inside this one, we instance first.
// => This is not only a copy, we need to instance. Otherwise, we'd be missing that branches
// => TODO: in the future, the instanciation is first, then the overides (to parameterize Composite, e.g. house with different wall colors)
const childrenComposite = compositeResource.composite.components.find((item) => item.name === CompositeRootComponent.componentName);
if (childrenComposite) {
for (const [childCompositeEntity, compositeRawData] of childrenComposite.data) {
const childComposite = getComponentValue(CompositeRootComponent, compositeRawData);
const childCompositePath = path.resolveComposite(childComposite.src, compositeDirectoryPath);
const childCompositeResource = compositeProvider.getCompositeOrNull(childCompositePath);
const targetEntity = getCompositeEntity(childCompositeEntity);
if (childCompositeResource) {
if (alreadyRequestedSrc.has(childCompositeResource.src) ||
childCompositeResource.src === compositeResource.src) {
throw new Error(`Composite ${compositeResource.src} has a recursive instanciation while try to instance ${childCompositeResource.src}. Previous instances: ${alreadyRequestedSrc.toString()}`);
}
instanceComposite(engine, childCompositeResource, compositeProvider, {
rootEntity: targetEntity,
alreadyRequestedSrc: new Set(alreadyRequestedSrc).add(childCompositeResource.src),
entityMapping: entityMapping?.type === EntityMappingMode.EMM_NEXT_AVAILABLE ? entityMapping : undefined
});
}
}
}
// ## 3 ##
// Then, we copy the all rest of the components (skipping the Composite ones)
for (const component of compositeResource.composite.components) {
// We already instanced the composite
if (component.name === CompositeRootComponent.componentName)
continue;
// ## 3a ##
// We find the component definition
const componentDefinition = getComponentDefinition(engine, component);
// ## 3b ##
// Iterating over all the entities with this component and create the replica
for (const [entity, compositeComponentValue] of component.data) {
const componentValueDeserialized = getComponentValue(componentDefinition, compositeComponentValue);
const targetEntity = getCompositeEntity(entity);
const componentValue = componentDefinition.create(targetEntity, componentValueDeserialized);
// ## 3c ##
// All entities referenced in the composite probably has a different resolved EntityNumber
// We'll know with the mappedEntityes
if (componentDefinition.componentId === TransformComponentNumber) {
const transform = componentValue;
if (transform.parent) {
transform.parent = getCompositeEntity(transform.parent);
}
else {
transform.parent = getCompositeEntity(0);
}
// TODO: is it going to be necessary to remap assets? e.g. src param from AudioSource and GltfContainer
}
else {
schemas_1.Schemas.mutateNestedValues(componentDefinition.schema.jsonSchema, componentValue, (value, valueType) => {
if (valueType.serializationType === 'entity') {
return { changed: true, value: getCompositeEntity(value) };
}
else {
return { changed: false };
}
});
}
}
}
const composite = CompositeRootComponent.getMutableOrNull(compositeRootEntity) || CompositeRootComponent.create(compositeRootEntity);
for (const [entitySource, targetEntity] of mappedEntities) {
composite.entities.push({
src: entitySource,
dest: targetEntity
});
}
composite.src = compositeResource.src;
return compositeRootEntity;
}
exports.instanceComposite = instanceComposite;