darker-engine
Version:
ecs lightweight library
602 lines (601 loc) • 29 kB
JavaScript
;
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.engine = void 0;
const uid_js_1 = require("./uid.js");
const engine = () => {
let rawSystems = []; // TODO: remove??
let systems = [];
let entityList = [];
let typeEntityMap = {};
let entityComponentMap = {};
// Contains which components/data has every entity
let entityDataMap = [];
// Contains which entities has every system
let systemEntitiesMap = [];
const { getUID } = (0, uid_js_1.uid)(() => entityList);
const setSystems = async (..._systems) => {
var _a, e_1, _b, _c;
var _d;
rawSystems = [..._systems];
systems = [];
systemEntitiesMap = [];
try {
for (var _e = true, _systems_1 = __asyncValues(_systems), _systems_1_1; _systems_1_1 = await _systems_1.next(), _a = _systems_1_1.done, !_a;) {
_c = _systems_1_1.value;
_e = false;
try {
const system = _c;
const _system = (await system());
_system.name = system.name;
_system.id = (_d = _system.id) !== null && _d !== void 0 ? _d : getUID(uid_js_1.UIDKey.SYSTEM);
systemEntitiesMap[_system.id] = [];
systems[_system.id] = _system;
}
finally {
_e = true;
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_e && !_a && (_b = _systems_1.return)) await _b.call(_systems_1);
}
finally { if (e_1) throw e_1.error; }
}
};
const _entity_removeComponent = async (entityId, component) => {
var _a, e_2, _b, _c;
var _d, _e;
const entity = getEntity(entityId);
if (!entity) {
return console.warn(`Warning "removeComponent::(${entityId},${component})" undefined`);
}
entityComponentMap[entityId] = entityComponentMap[entityId].filter((_component) => _component !== component);
const systemList = systems
.filter((system) => {
var _a;
const entities = systemEntitiesMap[system.id];
return (_a = entities === null || entities === void 0 ? void 0 : entities.includes(entityId)) !== null && _a !== void 0 ? _a : false;
})
.filter((system) => !system.components.every((_component) => { var _a; return (_a = entityComponentMap[entityId]) === null || _a === void 0 ? void 0 : _a.includes(_component); }))
.reverse();
try {
for (var _f = true, systemList_1 = __asyncValues(systemList), systemList_1_1; systemList_1_1 = await systemList_1.next(), _a = systemList_1_1.done, !_a;) {
_c = systemList_1_1.value;
_f = false;
try {
const system = _c;
const entities = systemEntitiesMap[system.id];
systemEntitiesMap[system.id] = (_d = entities === null || entities === void 0 ? void 0 : entities.filter((_id) => _id !== entityId)) !== null && _d !== void 0 ? _d : [];
try {
await ((_e = system === null || system === void 0 ? void 0 : system.onRemove) === null || _e === void 0 ? void 0 : _e.call(system, entityId));
}
catch (e) {
console.warn(`Error catch system::${system.id} "onRemove(${entityId})"`);
console.error(e);
}
}
finally {
_f = true;
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (!_f && !_a && (_b = systemList_1.return)) await _b.call(systemList_1);
}
finally { if (e_2) throw e_2.error; }
}
return;
};
const _entity_updateComponent = async (entityId, component, data) => {
var _a, e_3, _b, _c;
var _d, _e;
const entity = getEntity(entityId);
if (!entity) {
console.warn(`Warning "updateComponent::(${entityId},${component})" undefined`);
return entity;
}
if (!((_d = entityComponentMap[entityId]) === null || _d === void 0 ? void 0 : _d.includes(component))) {
return await _entity_addComponent(entityId, component, data);
}
const currentData = entityDataMap[entity.id];
entityDataMap[entity.id] = Object.assign(Object.assign({}, currentData), { [component]: Object.assign(Object.assign({}, (currentData[component] || {})), data) });
const systemList = systems
//Only filters the current updated component
.filter((system) => system.components.includes(component))
.filter((system) => {
var _a;
const entities = systemEntitiesMap[system.id];
return (_a = entities === null || entities === void 0 ? void 0 : entities.includes(entityId)) !== null && _a !== void 0 ? _a : false;
});
try {
for (var _f = true, systemList_2 = __asyncValues(systemList), systemList_2_1; systemList_2_1 = await systemList_2.next(), _a = systemList_2_1.done, !_a;) {
_c = systemList_2_1.value;
_f = false;
try {
const system = _c;
try {
await ((_e = system === null || system === void 0 ? void 0 : system.onUpdate) === null || _e === void 0 ? void 0 : _e.call(system, entityId, component));
}
catch (e) {
console.warn(`Error catch system::${system.id} "onUpdate(${entityId}, ${component})"`);
console.error(e);
}
}
finally {
_f = true;
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (!_f && !_a && (_b = systemList_2.return)) await _b.call(systemList_2);
}
finally { if (e_3) throw e_3.error; }
}
return entity;
};
const _entity_addComponent = async (entityId, component, data = {}) => {
var _a, e_4, _b, _c;
var _d;
const entity = getEntity(entityId);
if (!entity) {
console.warn(`Warning "addComponent::(${entityId},${component})" undefined`);
return entity;
}
entityComponentMap[entity.id].push(component);
entityDataMap[entity.id] = Object.assign(Object.assign({}, entityDataMap[entity.id]), { [component]: data });
const systemList = systems
.filter((system) => system.components.includes(component))
// Cuando los sistemas no contengan la entidad actual
.filter((system) => {
var _a;
const entities = systemEntitiesMap[system.id];
return (_a = !(entities === null || entities === void 0 ? void 0 : entities.includes(entityId))) !== null && _a !== void 0 ? _a : true;
})
// Cuando la entidad tenga los componentes correspondientes a ese sistema
.filter((system) => system.components.every((_component) => { var _a; return (_a = entityComponentMap[entityId]) === null || _a === void 0 ? void 0 : _a.includes(_component); }));
try {
for (var _e = true, systemList_3 = __asyncValues(systemList), systemList_3_1; systemList_3_1 = await systemList_3.next(), _a = systemList_3_1.done, !_a;) {
_c = systemList_3_1.value;
_e = false;
try {
const system = _c;
const entities = systemEntitiesMap[system.id];
systemEntitiesMap[system.id] = [...(entities !== null && entities !== void 0 ? entities : []), entityId];
try {
await ((_d = system === null || system === void 0 ? void 0 : system.onAdd) === null || _d === void 0 ? void 0 : _d.call(system, entityId));
}
catch (e) {
console.warn(`Error catch system::${system.id} "onAdd(${entityId})"`);
console.error(e);
}
}
finally {
_e = true;
}
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (!_e && !_a && (_b = systemList_3.return)) await _b.call(systemList_3);
}
finally { if (e_4) throw e_4.error; }
}
return entity;
};
// TODO: maintain original order, not this shit
const _entity_getComponentTypes = (entityId) => entityComponentMap[entityId];
const _entity_getComponent = (entityId, component, deepClone = false) => {
const entityData = entityDataMap[entityId];
return entityData && entityData[component]
? deepClone ? structuredClone(entityData[component]) : Object.assign({}, entityData[component])
: {};
};
const _entity_getComponents = (entityId, components, deepClone = false) => {
const entityData = entityDataMap[entityId];
return components.reduce((acc, component) => {
const data = entityData[component];
if (!data)
return acc;
acc[component] = deepClone ? structuredClone(data) : data;
return acc;
}, {});
};
const _entity_hasComponent = (entityId, component) => { var _a; return (_a = entityComponentMap[entityId]) === null || _a === void 0 ? void 0 : _a.includes(component); };
const _entity_getData = (entityId) => structuredClone(entityDataMap[entityId]);
const getEntityList = () => Object.values(entityList) || [];
const getEntityListByType = (type) => { var _a; return ((_a = typeEntityMap[type]) === null || _a === void 0 ? void 0 : _a.map((entityId) => entityList[entityId])) || []; };
const getEntityListByComponents = (...componentList) => {
return entityList
.reduce((list, entity) => {
const entityComponents = entityComponentMap[entity.id];
if (componentList.length === 0 ||
componentList.every((component) => entityComponents.includes(component))) {
return [...list, entity];
}
return list;
}, Array())
.filter(Boolean);
};
const getEntity = (entityId) => {
const entity = entityList[entityId];
if (!entity) {
console.warn(`Warning "internal::getEntity(${entityId})" undefined`);
}
return entity;
};
const addEntity = async (...rawEntities) => {
var _a, e_5, _b, _c, _d, e_6, _e, _f;
var _g;
const date = Date.now();
const entities = rawEntities.map((rawEntity) => {
var _a, _b, _c;
const entity = rawEntity;
entity.id = (_a = entity.id) !== null && _a !== void 0 ? _a : getUID(uid_js_1.UIDKey.ENTITY, entity.safe);
entity.getData = () => _entity_getData(entity.id);
entity.getComponent = (component, deepClone) => _entity_getComponent(entity.id, component, deepClone);
entity.getComponentTypes = () => _entity_getComponentTypes(entity.id);
entity.getComponents = (components, deepClone) => _entity_getComponents(entity.id, components, deepClone);
entity.hasComponent = (component) => _entity_hasComponent(entity.id, component);
entity.removeComponent = (component) => _entity_removeComponent(entity.id, component);
entity.updateComponent = (component, data) => _entity_updateComponent(entity.id, component, data);
if (!typeEntityMap[entity.type]) {
typeEntityMap[entity.type] = [];
}
typeEntityMap[entity.type].push(entity.id);
entityComponentMap[entity.id] = entity.components;
entityDataMap[entity.id] = (_c = (_b = entity === null || entity === void 0 ? void 0 : entity.getComponentTypes) === null || _b === void 0 ? void 0 : _b.call(entity).reduce((acc, b) => (Object.assign(Object.assign({}, acc), { [b]: acc[b] || {} })), entity.data)) !== null && _c !== void 0 ? _c : {};
entityList[entity.id] = entity;
return entity;
});
try {
for (var _h = true, entities_1 = __asyncValues(entities), entities_1_1; entities_1_1 = await entities_1.next(), _a = entities_1_1.done, !_a;) {
_c = entities_1_1.value;
_h = false;
try {
const entity = _c;
// Calculate points from component order.
const componentMapPoint = entity.components.reduce((obj, com, ind) => (Object.assign(Object.assign({}, obj), { [com]: ind ** 2 })), {});
const systemList = systems
.map((system) => ({
system,
// Assign system by component points order
points: system.components.reduce((points, component) => componentMapPoint[component] + points, 0),
}))
.filter((system) => !isNaN(system.points))
.sort((systemA, systemB) => systemB.points > systemA.points ? -1 : systemB.points === systemA.points ? 0 : 1)
.map(({ system }) => system);
try {
for (var _j = true, systemList_4 = (e_6 = void 0, __asyncValues(systemList)), systemList_4_1; systemList_4_1 = await systemList_4.next(), _d = systemList_4_1.done, !_d;) {
_f = systemList_4_1.value;
_j = false;
try {
const system = _f;
const systemEntities = systemEntitiesMap[system.id];
systemEntitiesMap[system.id] = [...(systemEntities !== null && systemEntities !== void 0 ? systemEntities : []), entity.id];
try {
await ((_g = system === null || system === void 0 ? void 0 : system.onAdd) === null || _g === void 0 ? void 0 : _g.call(system, entity.id));
}
catch (e) {
console.warn(`Error catch system::${system.id} "onUpdate(${entity.id})"`);
console.error(e);
}
}
finally {
_j = true;
}
}
}
catch (e_6_1) { e_6 = { error: e_6_1 }; }
finally {
try {
if (!_j && !_d && (_e = systemList_4.return)) await _e.call(systemList_4);
}
finally { if (e_6) throw e_6.error; }
}
}
finally {
_h = true;
}
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (!_h && !_a && (_b = entities_1.return)) await _b.call(entities_1);
}
finally { if (e_5) throw e_5.error; }
}
const ms = Date.now() - date;
if (ms > 500) {
console.warn(`addEntity ${ms}ms addedEntities:${entities.length} totalEntities:${entityList.length}`);
}
return entities;
};
const removeEntity = async (...entityIdList) => {
var _a, e_7, _b, _c, _d, e_8, _e, _f;
var _g, _h, _j;
const _entityList = entityIdList.map((entityId) => entityList[entityId]);
try {
for (var _k = true, _entityList_1 = __asyncValues(_entityList), _entityList_1_1; _entityList_1_1 = await _entityList_1.next(), _a = _entityList_1_1.done, !_a;) {
_c = _entityList_1_1.value;
_k = false;
try {
const entity = _c;
if (!entity)
return;
const componentEntityList = (_g = entity === null || entity === void 0 ? void 0 : entity.getComponentTypes) === null || _g === void 0 ? void 0 : _g.call(entity);
if (!componentEntityList)
return;
// Calculate points from component order.
const componentMapPoint = componentEntityList.reduce((obj, com, ind) => (Object.assign(Object.assign({}, obj), { [com]: ind ** 2 })), {});
const systemList = systems
.map((system) => ({
system,
// Assign system by component points order
points: system.components.reduce((points, component) => componentMapPoint[component] + points, 0),
}))
.filter((system) => !isNaN(system.points))
.sort((systemA, systemB) => systemB.points > systemA.points ? 1 : systemB.points === systemA.points ? 0 : -1)
.map(({ system }) => system);
try {
for (var _l = true, systemList_5 = (e_8 = void 0, __asyncValues(systemList)), systemList_5_1; systemList_5_1 = await systemList_5.next(), _d = systemList_5_1.done, !_d;) {
_f = systemList_5_1.value;
_l = false;
try {
const system = _f;
try {
await ((_h = system === null || system === void 0 ? void 0 : system.onRemove) === null || _h === void 0 ? void 0 : _h.call(system, entity.id));
}
catch (e) {
console.warn(`Error catch system::${system.id} "onRemove(${entity.id})"`);
console.error(e);
}
const systemEntities = systemEntitiesMap[system.id];
systemEntitiesMap[system.id] = (_j = systemEntities === null || systemEntities === void 0 ? void 0 : systemEntities.filter((_id) => _id !== entity.id)) !== null && _j !== void 0 ? _j : [];
}
finally {
_l = true;
}
}
}
catch (e_8_1) { e_8 = { error: e_8_1 }; }
finally {
try {
if (!_l && !_d && (_e = systemList_5.return)) await _e.call(systemList_5);
}
finally { if (e_8) throw e_8.error; }
}
delete entityList[entity.id];
delete entityComponentMap[entity.id];
typeEntityMap[entity.type] = typeEntityMap[entity.type].filter((entityId) => entity.id !== entityId);
}
finally {
_k = true;
}
}
}
catch (e_7_1) { e_7 = { error: e_7_1 }; }
finally {
try {
if (!_k && !_a && (_b = _entityList_1.return)) await _b.call(_entityList_1);
}
finally { if (e_7) throw e_7.error; }
}
};
const getSystem = (id) => systems[id];
const clear = () => {
systems = [];
entityList = [];
typeEntityMap = {};
entityComponentMap = [];
entityDataMap = [];
systemEntitiesMap = [];
};
const load = async () => {
var _a, e_9, _b, _c;
var _d;
try {
for (var _e = true, systems_1 = __asyncValues(systems), systems_1_1; systems_1_1 = await systems_1.next(), _a = systems_1_1.done, !_a;) {
_c = systems_1_1.value;
_e = false;
try {
const system = _c;
await ((_d = system === null || system === void 0 ? void 0 : system.onLoad) === null || _d === void 0 ? void 0 : _d.call(system));
}
finally {
_e = true;
}
}
}
catch (e_9_1) { e_9 = { error: e_9_1 }; }
finally {
try {
if (!_e && !_a && (_b = systems_1.return)) await _b.call(systems_1);
}
finally { if (e_9) throw e_9.error; }
}
};
const hardReload = async () => {
var _a, e_10, _b, _c, _d, e_11, _e, _f, _g, e_12, _h, _j, _k, e_13, _l, _m;
try {
for (var _o = true, _p = __asyncValues([...systems].reverse().filter(Boolean)), _q; _q = await _p.next(), _a = _q.done, !_a;) {
_c = _q.value;
_o = false;
try {
const system = _c;
const entities = systemEntitiesMap[system.id];
try {
for (var _r = true, _s = (e_11 = void 0, __asyncValues([...entities].reverse())), _t; _t = await _s.next(), _d = _t.done, !_d;) {
_f = _t.value;
_r = false;
try {
const entityId = _f;
(system === null || system === void 0 ? void 0 : system.onRemove) && (await (system === null || system === void 0 ? void 0 : system.onRemove(entityId)));
}
finally {
_r = true;
}
}
}
catch (e_11_1) { e_11 = { error: e_11_1 }; }
finally {
try {
if (!_r && !_d && (_e = _s.return)) await _e.call(_s);
}
finally { if (e_11) throw e_11.error; }
}
(system === null || system === void 0 ? void 0 : system.onDestroy) && (await (system === null || system === void 0 ? void 0 : system.onDestroy()));
}
finally {
_o = true;
}
}
}
catch (e_10_1) { e_10 = { error: e_10_1 }; }
finally {
try {
if (!_o && !_a && (_b = _p.return)) await _b.call(_p);
}
finally { if (e_10) throw e_10.error; }
}
try {
for (var _u = true, _v = __asyncValues([...systems].filter(Boolean)), _w; _w = await _v.next(), _g = _w.done, !_g;) {
_j = _w.value;
_u = false;
try {
const system = _j;
(system === null || system === void 0 ? void 0 : system.onLoad) && (await (system === null || system === void 0 ? void 0 : system.onLoad()));
const entities = systemEntitiesMap[system.id];
try {
for (var _x = true, _y = (e_13 = void 0, __asyncValues([...entities])), _z; _z = await _y.next(), _k = _z.done, !_k;) {
_m = _z.value;
_x = false;
try {
const entityId = _m;
(system === null || system === void 0 ? void 0 : system.onAdd) && (await (system === null || system === void 0 ? void 0 : system.onAdd(entityId)));
}
finally {
_x = true;
}
}
}
catch (e_13_1) { e_13 = { error: e_13_1 }; }
finally {
try {
if (!_x && !_k && (_l = _y.return)) await _l.call(_y);
}
finally { if (e_13) throw e_13.error; }
}
}
finally {
_u = true;
}
}
}
catch (e_12_1) { e_12 = { error: e_12_1 }; }
finally {
try {
if (!_u && !_g && (_h = _v.return)) await _h.call(_v);
}
finally { if (e_12) throw e_12.error; }
}
};
const debug = () => {
const swapSystem = async (systemId, system) => {
var _a, e_14, _b, _c, _d, e_15, _e, _f;
var _g, _h;
const currentSystem = systems[systemId];
const entities = systemEntitiesMap[systemId];
// Remove current entities from system
if (currentSystem.onRemove) {
try {
for (var _j = true, _k = __asyncValues([...entities].reverse()), _l; _l = await _k.next(), _a = _l.done, !_a;) {
_c = _l.value;
_j = false;
try {
const entityId = _c;
await currentSystem.onRemove(entityId);
}
finally {
_j = true;
}
}
}
catch (e_14_1) { e_14 = { error: e_14_1 }; }
finally {
try {
if (!_j && !_a && (_b = _k.return)) await _b.call(_k);
}
finally { if (e_14) throw e_14.error; }
}
}
// Destroy current system
if (currentSystem.onDestroy)
await currentSystem.onDestroy();
const swappedSystem = (await system());
swappedSystem.id = currentSystem.id;
swappedSystem.name = currentSystem.name;
systems[systemId] = swappedSystem;
// Load current system
if (currentSystem === null || currentSystem === void 0 ? void 0 : currentSystem.onLoad)
await ((_g = swappedSystem.onLoad) === null || _g === void 0 ? void 0 : _g.call(swappedSystem));
// Add current entities to swapped system
if (currentSystem.onAdd) {
try {
for (var _m = true, _o = __asyncValues([...entities]), _p; _p = await _o.next(), _d = _p.done, !_d;) {
_f = _p.value;
_m = false;
try {
const entityId = _f;
await ((_h = swappedSystem.onAdd) === null || _h === void 0 ? void 0 : _h.call(swappedSystem, entityId));
}
finally {
_m = true;
}
}
}
catch (e_15_1) { e_15 = { error: e_15_1 }; }
finally {
try {
if (!_m && !_d && (_e = _o.return)) await _e.call(_o);
}
finally { if (e_15) throw e_15.error; }
}
}
};
const getSystem = (name) => Object.values(systems).find((system) => system.name === name);
return {
swapSystem,
getSystem,
};
};
return {
setSystems,
getEntityList,
getEntityListByType,
getEntityListByComponents,
getEntity,
addEntity,
removeEntity,
getSystem,
clear,
load,
hardReload,
__debug__: debug(),
};
};
exports.engine = engine;