From 976a99baa1b0e4fd0a42bb0fa45454629ad5185e Mon Sep 17 00:00:00 2001 From: Matthieu Jolimaitre Date: Wed, 10 Apr 2024 02:45:34 +0200 Subject: [PATCH] refactor query into yielding api --- common/utils.ts | 2 +- server/components/display.ts | 19 +++++++++ server/components/world.ts | 4 +- server/engine.ts | 79 ++++++++++++++++++++---------------- server/entities/player.ts | 20 ++------- 5 files changed, 70 insertions(+), 54 deletions(-) diff --git a/common/utils.ts b/common/utils.ts index c9b4539..52c7e04 100644 --- a/common/utils.ts +++ b/common/utils.ts @@ -257,7 +257,7 @@ export function wait(ms: number) { } export async function run(cmd: string, ...args: string[]) { - const command = new Deno.Command(cmd, { args, stdout: "piped" }); + const command = new Deno.Command(cmd, { args, stdin: "inherit", stdout: "piped" }); const output = await command.output(); if (!output.success) throw new Error(); return new TextDecoder().decode(output.stdout); diff --git a/server/components/display.ts b/server/components/display.ts index 8004381..8f6f350 100644 --- a/server/components/display.ts +++ b/server/components/display.ts @@ -1,6 +1,25 @@ +import { v2 } from "../../common/utils.ts"; +import { range, Vec2 } from "../../common/utils.ts"; +import { Engine } from "../engine.ts"; +import { query_in_rect } from "./world.ts"; + export class CompDisplay { display; constructor(display: string) { this.display = display; } } + +export function sys_render_world(center: Vec2, size: Vec2) { + return (engine: Engine) => { + const radius = size.scale(0.5); + const result = Array.from(range(0, size.y())).map(() => Array.from(range(0, size.x())).map(() => " ")); + const min = center.sub(radius); + const max = center.add(radius).sub(v2(1, 1)); + for (const [pos, display] of engine.query_all(query_in_rect(min, max).with(CompDisplay))) { + const local_pos = pos.position.sub(min); + result[local_pos.y()][local_pos.x()] = display.display; + } + return result.map((line) => line.join("")).toReversed().join("\n"); + }; +} diff --git a/server/components/world.ts b/server/components/world.ts index 230c628..fb0857f 100644 --- a/server/components/world.ts +++ b/server/components/world.ts @@ -61,11 +61,11 @@ Deno.test("test_displacement", () => { }); export function query_at(pos: Vec2) { - return Query.filter(CompPos, (c) => c.position.overlaps(pos)); + return Query.with(CompPos).filter(([c]) => c.position.overlaps(pos)); } export function query_in_rect(min: Vec2, max: Vec2) { - return Query.filter(CompPos, (c) => c.position.inside(min, max)); + return Query.with(CompPos).filter(([c]) => c.position.inside(min, max)); } class CompStructure {} diff --git a/server/engine.ts b/server/engine.ts index c257905..e3e9758 100644 --- a/server/engine.ts +++ b/server/engine.ts @@ -60,11 +60,11 @@ export class Engine { } } - *query_all(query: Query) { + *query_all(query: Query) { yield* query.run(this.entities.values()); } - query_one(query: Query) { + query_one(query: Query) { return first(this.query_all(query)); } @@ -85,41 +85,52 @@ export class Engine { } } -export class Query { - private test; +type QueryRes = readonly T[]; +export class Query { + extractor; - constructor(test: (entity: Entity) => boolean) { - this.test = test; - } - - and(query: Query) { - return new Query((entity) => this.test(entity) && query.test(entity)); - } - - static with(...components: Constructible[]) { - return new Query((entity) => { - for (const comp of components) if (!entity.components.has(comp)) return false; - return true; - }); - } - - with(...components: Constructible[]) { - return this.and(Query.with(...components)); - } - - static filter(component: Constructible, filter: (comp: I, entity: Entity) => boolean) { - return new Query((entity) => { - const comp = entity.get(component); - if (comp === null) return false; - return filter(comp, entity); - }); - } - - filter(component: Constructible, filter: (comp: I, entity: Entity) => boolean) { - return this.and(Query.filter(component, filter)); + constructor(extractor: (entity: Entity) => O | null) { + this.extractor = extractor; } *run(entities: Iterable) { - for (const entity of entities) if (this.test(entity)) yield entity; + for (const entity of entities) { + const extracted = this.extractor(entity); + if (extracted !== null) yield extracted; + } + } + + and(other: Query) { + const this_extractor = this.extractor; + return new Query((entity) => { + const this_extracted = this_extractor(entity); + if (this_extracted === null) return null; + const other_extracted = other.extractor(entity); + if (other_extracted === null) return null; + return [...this_extracted, ...other_extracted] as const; + }); + } + + static with(comp: Constructible) { + function extract(entity: Entity) { + const extracted = entity.get(comp); + if (extracted === null) return null; + return [extracted] as const; + } + return new Query(extract); + } + + with(comp: Constructible) { + return this.and(Query.with(comp)); + } + + filter(predicate: (comps: O) => boolean) { + const this_extract = this.extractor; + return new Query((entity) => { + const extracted = this_extract(entity); + if (extracted === null) return null; + if (predicate(extracted)) return extracted; + else return null; + }); } } diff --git a/server/entities/player.ts b/server/entities/player.ts index 5f4d300..d08879d 100644 --- a/server/entities/player.ts +++ b/server/entities/player.ts @@ -1,7 +1,7 @@ import { mts } from "../../common/mod.ts"; -import { log_from, range, v2, Vec2 } from "../../common/utils.ts"; -import { CompDisplay } from "../components/display.ts"; -import { CompPos, query_in_rect, sys_find_free_pos } from "../components/world.ts"; +import { log_from, v2, Vec2 } from "../../common/utils.ts"; +import { CompDisplay, sys_render_world } from "../components/display.ts"; +import { CompPos, sys_find_free_pos } from "../components/world.ts"; import { Engine, Entity } from "../engine.ts"; import { ClientInterface } from "../network.ts"; const log = log_from(import.meta); @@ -58,20 +58,6 @@ export class Session { } } -export function sys_render_world(center: Vec2, size: Vec2) { - return (engine: Engine) => { - const radius = size.scale(0.5); - const result = Array.from(range(0, size.y())).map(() => Array.from(range(0, size.x())).map(() => " ")); - const min = center.sub(radius); - const max = center.add(radius); - for (const entity of engine.query_all(query_in_rect(min, max).with(CompDisplay))) { - const local_pos = entity.get_force(CompPos).position.sub(min); - result[local_pos.y()][local_pos.x()] = entity.get_force(CompDisplay).display; - } - return result.map((line) => line.join("")).toReversed().join("\n"); - }; -} - export function sys_spawn_player(position: Vec2) { return (engine: Engine) => { return engine.spawn((entity) =>