diff --git a/client/main.ts b/client/main.ts index 999bcc1..44c5720 100755 --- a/client/main.ts +++ b/client/main.ts @@ -80,17 +80,15 @@ class DisplayHandler { async spin() { while (true) { await wait(100); - const [width, height] = await DisplayHandler.get_res(); + const [width, height] = await this.get_res(); this.outputs.send({ kind: "request_display", content: { width, height } }); } } - static async get_res() { - const output = await run("stty", "size"); - const [raw_height, raw_width] = output.split(" "); - const width = Math.floor(parseInt(raw_width) / 2) - 2; // note : tiles are 2 char wide - const height = parseInt(raw_height) - 2; - return [width, height] as const; + async get_res() { + const width = parseInt(await run("tput", "cols")) / 2 - 2; // note : tiles are 2 char wide + const height = parseInt(await run("tput", "lines")) - 2; + return [width, height]; } refresh(raw: string) { @@ -125,13 +123,3 @@ class ServerInterface { } if (import.meta.main) await main(); - -Deno.test("res_check", async () => { - const ENABLED = false; // note : toggle before running test to know current resolution. - while (ENABLED) { - await wait(100); - const res = await DisplayHandler.get_res(); - console.clear(); - console.log("res", res); - } -}); diff --git a/common/utils.ts b/common/utils.ts index 52c7e04..c9b4539 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, stdin: "inherit", stdout: "piped" }); + const command = new Deno.Command(cmd, { args, 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 8f6f350..8004381 100644 --- a/server/components/display.ts +++ b/server/components/display.ts @@ -1,25 +1,6 @@ -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 fb0857f..2c55f57 100644 --- a/server/components/world.ts +++ b/server/components/world.ts @@ -1,5 +1,5 @@ -import { assertEquals } from "https://deno.land/std@0.221.0/assert/mod.ts"; -import { chunks, enumerate, log_from, spiral, Vec2 } from "../../common/utils.ts"; +import { spiral } from "../../common/utils.ts"; +import { chunks, enumerate, log_from, Vec2 } from "../../common/utils.ts"; import { Engine, Entity, Query } from "../engine.ts"; import { CompDisplay } from "./display.ts"; const log = log_from(import.meta); @@ -53,19 +53,12 @@ function* displacement_steps(position: Vec2, displacement: Vec2) { } } -Deno.test("test_displacement", () => { - assertEquals( - [...displacement_steps(new Vec2(1, 1), new Vec2(4, 6))], - [new Vec2(2, 2), new Vec2(3, 3), new Vec2(4, 4), new Vec2(5, 5), new Vec2(5, 6), new Vec2(5, 7)], - ); -}); - export function query_at(pos: Vec2) { - return Query.with(CompPos).filter(([c]) => c.position.overlaps(pos)); + return Query.filter(CompPos, (c) => c.position.overlaps(pos)); } export function query_in_rect(min: Vec2, max: Vec2) { - return Query.with(CompPos).filter(([c]) => c.position.inside(min, max)); + return Query.filter(CompPos, (c) => c.position.inside(min, max)); } class CompStructure {} diff --git a/server/engine.ts b/server/engine.ts index e3e9758..2d5b3b4 100644 --- a/server/engine.ts +++ b/server/engine.ts @@ -1,4 +1,9 @@ -import { Awaitable, ClassMap, Constructible, first, log_from, Prototyped, wait } from "../common/utils.ts"; +import { assertEquals } from "https://deno.land/std@0.221.0/assert/mod.ts"; +import { Awaitable, log_from, Prototyped, Vec2 } from "../common/utils.ts"; +import { ClassMap } from "../common/utils.ts"; +import { wait } from "../common/utils.ts"; +import { Constructible } from "../common/utils.ts"; +import { first } from "../common/utils.ts"; const log = log_from(import.meta); export class Entity { @@ -60,11 +65,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,52 +90,72 @@ export class Engine { } } -type QueryRes = readonly T[]; -export class Query { - extractor; +function* displacement_steps(position: Vec2, displacement: Vec2) { + let pos = position; + let left = displacement; + while (left.len() >= 1) { + if (left.x() >= 1) { + pos = pos.add(new Vec2(1, 0)); + left = left.sub(new Vec2(1, 0)); + } + if (left.x() <= -1) { + pos = pos.add(new Vec2(-1, 0)); + left = left.sub(new Vec2(-1, 0)); + } + if (left.y() >= 1) { + pos = pos.add(new Vec2(0, 1)); + left = left.sub(new Vec2(0, 1)); + } + if (left.y() <= -1) { + pos = pos.add(new Vec2(0, -1)); + left = left.sub(new Vec2(0, -1)); + } + yield pos; + } +} - constructor(extractor: (entity: Entity) => O | null) { - this.extractor = extractor; +Deno.test("test_displacement", () => { + assertEquals( + [...displacement_steps(new Vec2(1, 1), new Vec2(4, 6))], + [new Vec2(2, 2), new Vec2(3, 3), new Vec2(4, 4), new Vec2(5, 5), new Vec2(5, 6), new Vec2(5, 7)], + ); +}); + +export class Query { + private test; + + 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)); } *run(entities: Iterable) { - 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; - }); + for (const entity of entities) if (this.test(entity)) yield entity; } } diff --git a/server/entities/player.ts b/server/entities/player.ts index d08879d..a8ffebb 100644 --- a/server/entities/player.ts +++ b/server/entities/player.ts @@ -1,7 +1,9 @@ import { mts } from "../../common/mod.ts"; +import { range } from "../../common/utils.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 { CompDisplay } from "../components/display.ts"; +import { sys_find_free_pos } from "../components/world.ts"; +import { CompPos, query_in_rect } from "../components/world.ts"; import { Engine, Entity } from "../engine.ts"; import { ClientInterface } from "../network.ts"; const log = log_from(import.meta); @@ -58,6 +60,20 @@ 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) => diff --git a/server/main.ts b/server/main.ts index b848ce7..18357df 100755 --- a/server/main.ts +++ b/server/main.ts @@ -1,11 +1,12 @@ #!/bin/env -S deno run --allow-net --allow-read -import { log_from, v2 } from "../common/utils.ts"; +import { log_from, v2, wait } from "../common/utils.ts"; import { Engine } from "./engine.ts"; import { Session } from "./entities/player.ts"; -import { sys_spawn_structure } from "./components/world.ts"; +import { sys_spawn_obstacle, sys_spawn_structure } from "./components/world.ts"; import { Gateway } from "./network.ts"; import { sys_spawn_enemy } from "./entities/enemy.ts"; +import { spiral } from "../common/utils.ts"; const log = log_from(import.meta); async function main() { @@ -15,6 +16,14 @@ async function main() { engine.run(await sys_spawn_structure("../data/structures/houses.txt", v2(2, 2))); engine.run(sys_spawn_enemy(v2(1, 1))); + (async () => { + await wait(5_000); + for (const pos of spiral(v2(-8, -8))) { + await wait(500); + engine.run(sys_spawn_obstacle(pos, "@@")); + } + })(); + const port = 9999; const gateway = new Gateway(port); log("Awaiting connection."); diff --git a/server/network.ts b/server/network.ts index b23fe7d..719eb3a 100644 --- a/server/network.ts +++ b/server/network.ts @@ -1,7 +1,15 @@ #!/bin/env -S deno run --allow-net import { MsgToClient, MsgToServer, mts } from "../common/mod.ts"; -import { channel, log_from, parsed_stream, Receiver, Sender, serialized_stream } from "../common/utils.ts"; +import { + channel, + launch_caught, + log_from, + parsed_stream, + Receiver, + Sender, + serialized_stream, +} from "../common/utils.ts"; const log = log_from(import.meta); export class Gateway {