refactor: lifetimes

This commit is contained in:
cha0s 2024-07-21 03:54:03 -05:00
parent 8be6c23711
commit 1a7d55f6d3
5 changed files with 27 additions and 150 deletions

View File

@ -22,6 +22,8 @@ export default class Ecs {
deferredChanges = {}
destroying = new Set();
diff = {};
Systems = {};
@ -206,6 +208,10 @@ export default class Ecs {
}
destroy(entityId) {
this.destroying.add(entityId);
}
destroyImmediately(entityId) {
this.destroyMany([entityId]);
}
@ -427,31 +433,24 @@ export default class Ecs {
}
tick(elapsed) {
const destroying = new Set();
for (const systemName in this.Systems) {
const System = this.Systems[systemName];
if (System.active) {
if (System.frequency) {
System.elapsed += elapsed;
if (System.elapsed < System.frequency) {
continue;
}
}
while (!System.frequency || System.elapsed >= System.frequency) {
System.tick(System.frequency ? System.elapsed : elapsed);
for (let j = 0; j < System.destroying.length; j++) {
destroying.add(System.destroying[j]);
}
System.tickDestruction();
if (!System.frequency) {
break;
}
System.elapsed -= System.frequency;
}
if (!System.active) {
continue;
}
if (!System.frequency) {
System.tick(elapsed);
continue;
}
System.elapsed += elapsed;
while (System.elapsed >= System.frequency) {
System.tick(System.frequency);
System.elapsed -= System.frequency;
}
}
if (destroying.size > 0) {
this.destroyMany(destroying.values());
if (this.destroying.size > 0) {
this.destroyMany(this.destroying);
this.destroying.clear();
}
}

View File

@ -114,7 +114,7 @@ test('destroys entities', async () => {
expect(ecs.get(entity))
.to.be.undefined;
expect(() => {
ecs.destroy(entity);
ecs.destroyImmediately(entity);
})
.to.throw();
});
@ -170,50 +170,14 @@ test('ticks systems', async () => {
.to.deep.equal(JSON.stringify({y: 128 + 30}));
});
test('creates many entities when ticking systems', () => {
const ecs = new Ecs({
Systems: {
Spawn: class extends System {
tick() {
this.createManyEntities(Array.from({length: 5}).map(() => []));
}
},
},
});
ecs.system('Spawn').active = true;
ecs.create();
expect(ecs.get(5))
.to.be.undefined;
ecs.tick(1);
expect(ecs.get(5))
.to.not.be.undefined;
});
test('creates entities when ticking systems', () => {
const ecs = new Ecs({
Systems: {
Spawn: class extends System {
tick() {
this.createEntity();
}
},
},
});
ecs.system('Spawn').active = true;
ecs.create();
expect(ecs.get(2))
.to.be.undefined;
ecs.tick(1);
expect(ecs.get(2))
.to.not.be.undefined;
});
test('schedules entities to be deleted when ticking systems', () => {
const ecs = new Ecs({
Systems: {
Despawn: class extends System {
tick() {
this.destroyEntity(1);
this.ecs.destroy(1);
expect(ecs.get(1))
.to.not.be.undefined;
}
},
},
@ -225,50 +189,6 @@ test('schedules entities to be deleted when ticking systems', () => {
.to.be.undefined;
});
test('adds components to and remove components from entities when ticking systems', async () => {
let promise;
const ecs = new Ecs({
Components: {Foo: wrapProperties('Foo', {bar: {type: 'uint8'}})},
Systems: {
AddComponent: class extends System {
static queries() {
return {
default: ['Foo'],
};
}
tick() {
promise = this.insertComponents(1, {Foo: {}});
}
},
RemoveComponent: class extends System {
static queries() {
return {
default: ['Foo'],
};
}
tick() {
this.removeComponents(1, ['Foo']);
}
},
},
});
ecs.system('AddComponent').active = true;
ecs.create();
ecs.tick(1);
await promise;
expect(Array.from(ecs.system('AddComponent').select('default')).length)
.to.equal(1);
expect(ecs.get(1).Foo)
.to.not.be.undefined;
ecs.system('AddComponent').active = false;
ecs.system('RemoveComponent').active = true;
ecs.tick(1);
expect(Array.from(ecs.system('RemoveComponent').select('default')).length)
.to.equal(0);
expect(ecs.get(1).Foo)
.to.be.undefined;
});
test('generates diffs for entity creation', async () => {
const ecs = new Ecs();
let entity;
@ -347,7 +267,7 @@ test('generates diffs for deletions', async () => {
let entity;
entity = await ecs.create();
ecs.setClean();
ecs.destroy(entity);
ecs.destroyImmediately(entity);
expect(ecs.diff)
.to.deep.equal({[entity]: false});
});

View File

@ -6,8 +6,6 @@ export default class System {
active = false;
destroying = [];
ecs;
elapsed = 0;
@ -25,38 +23,12 @@ export default class System {
this.reindex(ecs.entities);
}
createEntity(components) {
return this.ecs.create(components);
}
createManyEntities(componentsList) {
return this.ecs.createMany(componentsList);
}
deindex(entityIds) {
for (const i in this.queries) {
this.queries[i].deindex(entityIds);
}
}
destroyEntity(entityId) {
this.destroyManyEntities([entityId]);
}
destroyManyEntities(entityIds) {
for (let i = 0; i < entityIds.length; i++) {
this.destroying.push(entityIds[i]);
}
}
async insertComponents(entityId, components) {
return this.ecs.insert(entityId, components);
}
async insertManyComponents(components) {
return this.ecs.insertMany(components);
}
static get priority() {
return {
phase: 'normal',
@ -73,14 +45,6 @@ export default class System {
}
}
removeComponents(entityId, components) {
this.ecs.remove(entityId, components);
}
removeManyComponents(entityIds) {
this.ecs.removeMany(entityIds);
}
select(query) {
return this.queries[query].select();
}
@ -117,13 +81,6 @@ export default class System {
);
}
tickDestruction() {
if (this.destroying.length > 0) {
this.deindex(this.destroying);
}
this.destroying = [];
}
tick() {}
}

View File

@ -322,6 +322,7 @@ export default class Engine {
stop() {
clearTimeout(this.handle);
this.handle = undefined;
this.tick(0);
}
tick(elapsed) {

View File

@ -45,8 +45,8 @@ const engine = new Engine(WorkerServer);
onmessage = async (event) => {
if (0 === event.data) {
engine.stop();
await engine.disconnectPlayer(0);
engine.stop();
await engine.saveEcses();
postMessage(0);
return;