feat: System priority

This commit is contained in:
cha0s 2024-06-25 10:43:12 -05:00
parent 4ba38af246
commit cadabc219d
3 changed files with 130 additions and 1 deletions

View File

@ -1,3 +1,6 @@
import System from '@/ecs/system.js';
import gather from '@/util/gather.js';
export default gather(import.meta.glob('./*.js', {eager: true, import: 'default'}));
const gathered = gather(import.meta.glob('./*.js', {eager: true, import: 'default'}));
export default System.sort(gathered);

View File

@ -1,3 +1,4 @@
import Digraph from '@/util/digraph.js';
import Query from './query.js';
@ -54,6 +55,12 @@ export default class System {
this.ecs.insertMany(components);
}
static get priority() {
return {
phase: 'normal',
}
}
static queries() {
return {};
}
@ -76,6 +83,38 @@ export default class System {
return this.queries[query].select();
}
static sort(Systems) {
const phases = {
'pre': new Digraph(),
'normal': new Digraph(),
'post': new Digraph(),
};
for (const systemName in Systems) {
const {priority} = Systems[systemName];
const phase = phases[priority.phase || 'normal'];
phase.ensureTail(systemName);
if (priority.before) {
for (const before of Array.isArray(priority.before) ? priority.before : [priority.before]) {
phase.addDependency(before, systemName);
}
}
if (priority.after) {
for (const after of Array.isArray(priority.after) ? priority.after : [priority.after]) {
phase.addDependency(systemName, after);
}
}
}
const sorted = [
...phases['pre'].sort(),
...phases['normal'].sort(),
...phases['post'].sort(),
];
return Object.fromEntries(
Object.entries(Systems)
.toSorted(([l], [r]) => sorted.indexOf(l) - sorted.indexOf(r)),
);
}
tickDestruction() {
this.deindex(this.destroying);
this.destroying = [];

87
app/util/digraph.js Normal file
View File

@ -0,0 +1,87 @@
export default class Digraph {
arcs = new Map();
addDependency(head, tail) {
this.ensureTail(head);
this.ensureTail(tail).add(head);
}
detectCycles() {
const cycles = [];
const visited = new Set();
const walking = new Set();
const walk = (vertex) => {
if (!visited.has(vertex)) {
visited.add(vertex);
walking.add(vertex);
const it = this.neighbors(vertex);
for (let current = it.next(); true !== current.done; current = it.next()) {
const {value: neighbor} = current;
if (!visited.has(neighbor)) {
walk(neighbor);
}
else if (walking.has(neighbor)) {
cycles.push([vertex, neighbor]);
}
}
}
walking.delete(vertex);
};
const {tails} = this;
for (let current = tails.next(); true !== current.done; current = tails.next()) {
walk(current.value);
}
return cycles;
}
ensureTail(tail) {
if (!this.arcs.has(tail)) {
this.arcs.set(tail, new Set());
}
return this.arcs.get(tail);
}
neighbors(vertex) {
return this.arcs.get(vertex).values();
}
sort() {
const visited = new Set();
const scores = new Map();
const walk = (vertex, score) => {
visited.add(vertex);
const neighbors = this.neighbors(vertex);
for (let current = neighbors.next(); true !== current.done; current = neighbors.next()) {
const {value: neighbor} = current;
if (!visited.has(neighbor)) {
score = walk(neighbor, score);
}
}
scores.set(vertex, score);
return score - 1;
};
let score = this.arcs.size - 1;
const {tails} = this;
for (let current = tails.next(); true !== current.done; current = tails.next()) {
const {value: vertex} = current;
if (!visited.has(vertex)) {
score = walk(vertex, score);
}
}
return Array.from(scores.entries())
.sort(([, l], [, r]) => l - r)
.map(([vertex]) => vertex);
}
removeDependency(head, tail) {
if (this.arcs.has(tail)) {
this.arcs.get(tail).delete(head);
}
}
get tails() {
return this.arcs.keys();
}
}