perf: detached entities
This commit is contained in:
parent
097bf9505f
commit
172b457a8c
|
@ -73,7 +73,7 @@ export default class Component {
|
|||
}
|
||||
|
||||
destroy(entityId) {
|
||||
this.destroyMany([entityId]);
|
||||
this.destroyMany(new Set([entityId]));
|
||||
}
|
||||
|
||||
destroyMany(entityIds) {
|
||||
|
|
|
@ -25,6 +25,8 @@ export default class Ecs {
|
|||
|
||||
$$destructionDependencies = new Map();
|
||||
|
||||
$$detached = new Set();
|
||||
|
||||
diff = {};
|
||||
|
||||
Systems = {};
|
||||
|
@ -58,7 +60,7 @@ export default class Ecs {
|
|||
|
||||
async apply(patch) {
|
||||
const creating = [];
|
||||
const destroying = [];
|
||||
const destroying = new Set();
|
||||
const inserting = [];
|
||||
const removing = [];
|
||||
const updating = [];
|
||||
|
@ -66,7 +68,7 @@ export default class Ecs {
|
|||
const entityId = parseInt(entityIdString);
|
||||
const components = patch[entityId];
|
||||
if (false === components) {
|
||||
destroying.push(entityId);
|
||||
destroying.add(entityId);
|
||||
continue;
|
||||
}
|
||||
const componentsToRemove = [];
|
||||
|
@ -105,7 +107,7 @@ export default class Ecs {
|
|||
creating.push([entityId, componentsToUpdate]);
|
||||
}
|
||||
}
|
||||
if (destroying.length > 0) {
|
||||
if (destroying.size > 0) {
|
||||
this.destroyMany(destroying);
|
||||
}
|
||||
const promises = [];
|
||||
|
@ -124,6 +126,13 @@ export default class Ecs {
|
|||
}
|
||||
}
|
||||
|
||||
attach(entityIds) {
|
||||
for (const entityId of entityIds) {
|
||||
this.$$detached.delete(entityId);
|
||||
}
|
||||
this.reindex(entityIds);
|
||||
}
|
||||
|
||||
changed(criteria) {
|
||||
const it = Object.entries(this.diff).values();
|
||||
return {
|
||||
|
@ -158,10 +167,26 @@ export default class Ecs {
|
|||
return entityId;
|
||||
}
|
||||
|
||||
async createDetached(components = {}) {
|
||||
const [entityId] = await this.createManyDetached([components]);
|
||||
return entityId;
|
||||
}
|
||||
|
||||
async createMany(componentsList) {
|
||||
const specificsList = [];
|
||||
for (const components of componentsList) {
|
||||
specificsList.push([this.$$caret++, components]);
|
||||
specificsList.push([this.$$caret, components]);
|
||||
this.$$caret += 1;
|
||||
}
|
||||
return this.createManySpecific(specificsList);
|
||||
}
|
||||
|
||||
async createManyDetached(componentsList) {
|
||||
const specificsList = [];
|
||||
for (const components of componentsList) {
|
||||
specificsList.push([this.$$caret, components]);
|
||||
this.$$detached.add(this.$$caret);
|
||||
this.$$caret += 1;
|
||||
}
|
||||
return this.createManySpecific(specificsList);
|
||||
}
|
||||
|
@ -170,18 +195,20 @@ export default class Ecs {
|
|||
if (0 === specificsList.length) {
|
||||
return;
|
||||
}
|
||||
const entityIds = [];
|
||||
const entityIds = new Set();
|
||||
const creating = {};
|
||||
for (let i = 0; i < specificsList.length; i++) {
|
||||
const [entityId, components] = specificsList[i];
|
||||
this.deferredChanges[entityId] = [];
|
||||
if (!this.$$detached.has(entityId)) {
|
||||
this.deferredChanges[entityId] = [];
|
||||
}
|
||||
const componentNames = [];
|
||||
for (const componentName in components) {
|
||||
if (this.Components[componentName]) {
|
||||
componentNames.push(componentName);
|
||||
}
|
||||
}
|
||||
entityIds.push(entityId);
|
||||
entityIds.add(entityId);
|
||||
this.rebuild(entityId, () => componentNames);
|
||||
for (const componentName of componentNames) {
|
||||
if (!creating[componentName]) {
|
||||
|
@ -198,6 +225,9 @@ export default class Ecs {
|
|||
await Promise.all(promises);
|
||||
for (let i = 0; i < specificsList.length; i++) {
|
||||
const [entityId] = specificsList[i];
|
||||
if (this.$$detached.has(entityId)) {
|
||||
continue;
|
||||
}
|
||||
const changes = this.deferredChanges[entityId];
|
||||
delete this.deferredChanges[entityId];
|
||||
for (const components of changes) {
|
||||
|
@ -209,16 +239,26 @@ export default class Ecs {
|
|||
}
|
||||
|
||||
async createSpecific(entityId, components) {
|
||||
return this.createManySpecific([[entityId, components]]);
|
||||
const [created] = await this.createManySpecific([[entityId, components]]);
|
||||
return created;
|
||||
}
|
||||
|
||||
deindex(entityIds) {
|
||||
// Stage 4 Draft / July 6, 2024
|
||||
// const attached = entityIds.difference(this.$$detached);
|
||||
const attached = new Set(entityIds);
|
||||
for (const detached of this.$$detached) {
|
||||
attached.delete(detached);
|
||||
}
|
||||
if (0 === attached.size) {
|
||||
return;
|
||||
}
|
||||
for (const systemName in this.Systems) {
|
||||
const System = this.Systems[systemName];
|
||||
if (!System.active) {
|
||||
continue;
|
||||
}
|
||||
System.deindex(entityIds);
|
||||
System.deindex(attached);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,10 +299,6 @@ export default class Ecs {
|
|||
return dependencies.resolvers.promise;
|
||||
}
|
||||
|
||||
destroyAll() {
|
||||
this.destroyMany(this.entities);
|
||||
}
|
||||
|
||||
destroyMany(entityIds) {
|
||||
const destroying = {};
|
||||
this.deindex(entityIds);
|
||||
|
@ -286,6 +322,13 @@ export default class Ecs {
|
|||
}
|
||||
}
|
||||
|
||||
detach(entityIds) {
|
||||
this.deindex(entityIds);
|
||||
for (const entityId of entityIds) {
|
||||
this.$$detached.add(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
get entities() {
|
||||
const ids = [];
|
||||
for (const entity of Object.values(this.$$entities)) {
|
||||
|
@ -327,6 +370,9 @@ export default class Ecs {
|
|||
}
|
||||
|
||||
markChange(entityId, components) {
|
||||
if (this.$$detached.has(entityId)) {
|
||||
return;
|
||||
}
|
||||
if (this.deferredChanges[entityId]) {
|
||||
this.deferredChanges[entityId].push(components);
|
||||
return;
|
||||
|
@ -415,12 +461,21 @@ export default class Ecs {
|
|||
}
|
||||
|
||||
reindex(entityIds) {
|
||||
// Stage 4 Draft / July 6, 2024
|
||||
// const attached = entityIds.difference(this.$$detached);
|
||||
const attached = new Set(entityIds);
|
||||
for (const detached of this.$$detached) {
|
||||
attached.delete(detached);
|
||||
}
|
||||
if (0 === attached.size) {
|
||||
return;
|
||||
}
|
||||
for (const systemName in this.Systems) {
|
||||
const System = this.Systems[systemName];
|
||||
if (!System.active) {
|
||||
continue;
|
||||
}
|
||||
System.reindex(entityIds);
|
||||
System.reindex(attached);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,11 +110,11 @@ test('destroys entities', async () => {
|
|||
.to.deep.equal(JSON.stringify({Empty: {}, Position: {y: 128}}));
|
||||
expect(ecs.get(entity))
|
||||
.to.not.be.undefined;
|
||||
ecs.destroyAll();
|
||||
ecs.destroyMany(new Set([entity]));
|
||||
expect(ecs.get(entity))
|
||||
.to.be.undefined;
|
||||
expect(() => {
|
||||
ecs.destroyMany([entity]);
|
||||
ecs.destroyMany(new Set([entity]));
|
||||
})
|
||||
.to.throw();
|
||||
});
|
||||
|
@ -122,10 +122,10 @@ test('destroys entities', async () => {
|
|||
test('inserts components into entities', async () => {
|
||||
const ecs = new Ecs({Components: {Empty, Position}});
|
||||
const entity = await ecs.create({Empty: {}});
|
||||
ecs.insert(entity, {Position: {y: 128}});
|
||||
await ecs.insert(entity, {Position: {y: 128}});
|
||||
expect(JSON.stringify(ecs.get(entity)))
|
||||
.to.deep.equal(JSON.stringify({Empty: {}, Position: {y: 128}}));
|
||||
ecs.insert(entity, {Position: {y: 64}});
|
||||
await ecs.insert(entity, {Position: {y: 64}});
|
||||
expect(JSON.stringify(ecs.get(entity)))
|
||||
.to.deep.equal(JSON.stringify({Empty: {}, Position: {y: 64}}));
|
||||
});
|
||||
|
@ -197,6 +197,35 @@ test('schedules entities to be deleted when ticking systems', async () => {
|
|||
.to.be.undefined;
|
||||
});
|
||||
|
||||
test('skips indexing detached entities', async () => {
|
||||
const ecs = new Ecs({
|
||||
Components: {Empty},
|
||||
Systems: {
|
||||
Indexer: class extends System {
|
||||
static queries() {
|
||||
return {
|
||||
default: ['Empty'],
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
const {$$index: index} = ecs.system('Indexer').queries.default;
|
||||
ecs.system('Indexer').active = true;
|
||||
const attached = await ecs.create({Empty: {}});
|
||||
expect(Array.from(index))
|
||||
.to.deep.equal([attached]);
|
||||
ecs.destroyMany(new Set([attached]));
|
||||
expect(Array.from(index))
|
||||
.to.deep.equal([]);
|
||||
const detached = await ecs.createDetached({Empty: {}});
|
||||
expect(Array.from(index))
|
||||
.to.deep.equal([]);
|
||||
ecs.destroyMany(new Set([detached]));
|
||||
expect(Array.from(index))
|
||||
.to.deep.equal([]);
|
||||
});
|
||||
|
||||
test('generates diffs for entity creation', async () => {
|
||||
const ecs = new Ecs();
|
||||
let entity;
|
||||
|
@ -210,7 +239,7 @@ test('generates diffs for adding and removing components', async () => {
|
|||
let entity;
|
||||
entity = await ecs.create();
|
||||
ecs.setClean();
|
||||
ecs.insert(entity, {Position: {x: 64}});
|
||||
await ecs.insert(entity, {Position: {x: 64}});
|
||||
expect(ecs.diff)
|
||||
.to.deep.equal({[entity]: {Position: {x: 64}}});
|
||||
ecs.setClean();
|
||||
|
@ -246,6 +275,23 @@ test('generates diffs for entity mutations', async () => {
|
|||
.to.deep.equal({});
|
||||
});
|
||||
|
||||
test('generates no diffs for detached entities', async () => {
|
||||
const ecs = new Ecs({Components: {Position}});
|
||||
let entity;
|
||||
entity = await ecs.createDetached();
|
||||
expect(ecs.diff)
|
||||
.to.deep.equal({});
|
||||
await ecs.insert(entity, {Position: {x: 64}});
|
||||
expect(ecs.diff)
|
||||
.to.deep.equal({});
|
||||
ecs.get(entity).Position.x = 128;
|
||||
expect(ecs.diff)
|
||||
.to.deep.equal({});
|
||||
ecs.remove(entity, ['Position']);
|
||||
expect(ecs.diff)
|
||||
.to.deep.equal({});
|
||||
});
|
||||
|
||||
test('generates coalesced diffs for components', async () => {
|
||||
const ecs = new Ecs({Components: {Position}});
|
||||
let entity;
|
||||
|
@ -253,7 +299,7 @@ test('generates coalesced diffs for components', async () => {
|
|||
ecs.remove(entity, ['Position']);
|
||||
expect(ecs.diff)
|
||||
.to.deep.equal({[entity]: {Position: false}});
|
||||
ecs.insert(entity, {Position: {}});
|
||||
await ecs.insert(entity, {Position: {}});
|
||||
expect(ecs.diff)
|
||||
.to.deep.equal({[entity]: {Position: {}}});
|
||||
});
|
||||
|
|
|
@ -20,7 +20,6 @@ export default class System {
|
|||
for (const i in queries) {
|
||||
this.queries[i] = new Query(queries[i], ecs);
|
||||
}
|
||||
this.reindex(ecs.entities);
|
||||
}
|
||||
|
||||
deindex(entityIds) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user