import { System, SystemType } from "./system"; import { Entity } from "./entity"; import { ComponentType } from "./component"; export class Game { private running: boolean; private last_update: number; private readonly entities: Map = new Map(); private readonly component_entities: Map> = new Map(); private readonly systems: Map = new Map(); private readonly system_order: SystemType[]; constructor( public readonly client_id: string, systems: System[], ) { this.last_update = performance.now(); this.running = false; systems.forEach((system) => this.systems.set(system.system_type, system)); this.system_order = systems.map(({ system_type }) => system_type); } public start() { if (this.running) return; console.log("starting game"); this.running = true; this.last_update = performance.now(); const game_loop = (timestamp: number) => { if (!this.running) return; // rebuild component -> { entity } map this.component_entities.clear(); Array.from(this.entities.values()).forEach((entity) => Object.values(entity.components).forEach((component) => { const set = this.component_entities.get(component.name) ?? new Set(); set.add(entity.id); this.component_entities.set(component.name, set); }), ); const dt = timestamp - this.last_update; this.system_order.forEach((system_type) => this.systems.get(system_type)!.update(dt, this), ); this.last_update = timestamp; requestAnimationFrame(game_loop); // tail call recursion! /s }; requestAnimationFrame(game_loop); } public stop() { if (!this.running) return; this.running = false; } public for_each_entity_with_component( component: ComponentType, callback: (entity: Entity) => void, ) { this.component_entities.get(component)?.forEach((entity_id) => { const entity = this.entities.get(entity_id); if (!entity) return; callback(entity); }); } public get_entity(id: string) { return this.entities.get(id); } public put_entity(entity: Entity) { const old_entity = this.entities.get(entity.id); if (old_entity) this.clear_entity_components(old_entity); Object.values(entity.components).forEach((component) => { const set = this.component_entities.get(component.name) ?? new Set(); set.add(entity.id); this.component_entities.set(component.name, set); }); this.entities.set(entity.id, entity); } public remove_entity(id: string) { const entity = this.entities.get(id); if (typeof entity === "undefined") return; this.clear_entity_components(entity); this.entities.delete(id); } private clear_entity_components(entity: Entity) { Object.values(entity.components).forEach((component) => { const set = this.component_entities.get(component.name); if (typeof set === "undefined") return; set.delete(entity.id); }); } public get_system(system_type: SystemType): T | undefined { return this.systems.get(system_type) as T; } }