From 16a094806e0ec3878fb4a207aa82654211b36b5f Mon Sep 17 00:00:00 2001 From: cha0s Date: Sun, 4 Aug 2024 20:54:56 -0500 Subject: [PATCH] refactor: indexing --- app/ecs/ecs.js | 64 ++++++++++++++++++++++++++---------------------- app/ecs/query.js | 26 ++++++-------------- 2 files changed, 42 insertions(+), 48 deletions(-) diff --git a/app/ecs/ecs.js b/app/ecs/ecs.js index 41e3238..e46b520 100644 --- a/app/ecs/ecs.js +++ b/app/ecs/ecs.js @@ -23,18 +23,22 @@ export default class Ecs { deferredChanges = {} + $$deindexing = new Set(); + $$destructionDependencies = new Map(); $$detached = new Set(); diff = {}; - Systems = {}; - $$entities = {}; $$entityFactory = new EntityFactory(); + $$reindexing = new Set(); + + Systems = {}; + constructor({Systems, Components} = {}) { for (const componentName in Components) { this.Components[componentName] = new Components[componentName](this); @@ -129,8 +133,8 @@ export default class Ecs { attach(entityIds) { for (const entityId of entityIds) { this.$$detached.delete(entityId); + this.$$reindexing.add(entityId); } - this.reindex(entityIds); } changed(criteria) { @@ -209,6 +213,7 @@ export default class Ecs { } } entityIds.add(entityId); + this.$$reindexing.add(entityId); this.rebuild(entityId, () => componentNames); for (const componentName of componentNames) { if (!creating[componentName]) { @@ -234,7 +239,6 @@ export default class Ecs { this.markChange(entityId, components); } } - this.reindex(entityIds); return entityIds; } @@ -301,8 +305,9 @@ export default class Ecs { destroyMany(entityIds) { const destroying = {}; - this.deindex(entityIds); + // this.deindex(entityIds); for (const entityId of entityIds) { + this.$$deindexing.add(entityId); if (!this.$$entities[entityId]) { throw new Error(`can't destroy non-existent entity ${entityId}`); } @@ -323,8 +328,8 @@ export default class Ecs { } detach(entityIds) { - this.deindex(entityIds); for (const entityId of entityIds) { + this.$$deindexing.add(entityId); this.$$detached.add(entityId); } } @@ -358,6 +363,7 @@ export default class Ecs { diff[componentName] = {}; inserting[componentName].push([entityId, components[componentName]]); } + this.$$reindexing.add(entityId); unique.add(entityId); this.markChange(entityId, diff); } @@ -366,7 +372,6 @@ export default class Ecs { promises.push(this.Components[componentName].insertMany(inserting[componentName])); } await Promise.all(promises); - this.reindex(unique); } markChange(entityId, components) { @@ -383,13 +388,7 @@ export default class Ecs { } // Created? else if (!this.diff[entityId]) { - const filtered = {}; - for (const componentName in components) { - filtered[componentName] = false === components[componentName] - ? false - : components[componentName]; - } - this.diff[entityId] = filtered; + this.diff[entityId] = components; } // Otherwise, merge. else { @@ -487,6 +486,7 @@ export default class Ecs { const removing = {}; const unique = new Set(); for (const [entityId, components] of entities) { + this.$$reindexing.add(entityId); unique.add(entityId); const diff = {}; for (const componentName of components) { @@ -502,7 +502,6 @@ export default class Ecs { for (const componentName in removing) { this.Components[componentName].destroyMany(removing[componentName]); } - this.reindex(unique); } static serialize(ecs, view) { @@ -523,6 +522,26 @@ export default class Ecs { } tick(elapsed) { + // destroy entities + const destroying = new Set(); + for (const [entityId, {promises}] of this.$$destructionDependencies) { + if (0 === promises.size) { + destroying.add(entityId); + } + } + if (destroying.size > 0) { + this.destroyMany(destroying); + for (const entityId of destroying) { + this.$$destructionDependencies.get(entityId).resolvers.resolve(); + this.$$destructionDependencies.delete(entityId); + } + } + // update indices + this.deindex(this.$$deindexing); + this.$$deindexing.clear(); + this.reindex(this.$$reindexing); + this.$$reindexing.clear(); + // tick systems for (const systemName in this.Systems) { const System = this.Systems[systemName]; if (!System.active) { @@ -538,19 +557,6 @@ export default class Ecs { System.elapsed -= System.frequency; } } - const destroying = new Set(); - for (const [entityId, {promises}] of this.$$destructionDependencies) { - if (0 === promises.size) { - destroying.add(entityId); - } - } - if (destroying.size > 0) { - this.destroyMany(destroying); - for (const entityId of destroying) { - this.$$destructionDependencies.get(entityId).resolvers.resolve(); - this.$$destructionDependencies.delete(entityId); - } - } } toJSON() { @@ -583,6 +589,7 @@ export default class Ecs { } updating[componentName].push([entityId, components[componentName]]); } + this.$$reindexing.add(entityId); unique.add(entityId); } const promises = []; @@ -590,7 +597,6 @@ export default class Ecs { promises.push(this.Components[componentName].updateMany(updating[componentName])); } await Promise.all(promises); - this.reindex(unique); } } diff --git a/app/ecs/query.js b/app/ecs/query.js index ec5ed2b..42f3abf 100644 --- a/app/ecs/query.js +++ b/app/ecs/query.js @@ -2,7 +2,7 @@ export default class Query { $$criteria = {with: [], without: []}; $$ecs; - $$index = new Set(); + $$map = new Map(); constructor(parameters, ecs) { this.$$ecs = ecs; @@ -20,19 +20,19 @@ export default class Query { } get count() { - return this.$$index.size; + return this.$$map.size; } deindex(entityIds) { for (const entityId of entityIds) { - this.$$index.delete(entityId); + this.$$map.delete(entityId); } } reindex(entityIds) { if (0 === this.$$criteria.with.length && 0 === this.$$criteria.without.length) { for (const entityId of entityIds) { - this.$$index.add(entityId); + this.$$map.set(entityId, this.$$ecs.get(entityId)); } return; } @@ -53,28 +53,16 @@ export default class Query { } } if (should) { - this.$$index.add(entityId); + this.$$map.set(entityId, this.$$ecs.get(entityId)); } else if (!should) { - this.$$index.delete(entityId); + this.$$map.delete(entityId); } } } select() { - const it = this.$$index.values(); - return { - [Symbol.iterator]() { - return this; - }, - next: () => { - const result = it.next(); - if (result.done) { - return {done: true, value: undefined}; - } - return {done: false, value: this.$$ecs.get(result.value)}; - }, - }; + return this.$$map.values(); } }