Implement main game as a mod

This commit is contained in:
2026-09-24 23:49:34 -03:00
parent bb42dd662e
commit 6458bc0440
131 changed files with 1810 additions and 786 deletions
+65
View File
@@ -0,0 +1,65 @@
import { resolve, toFileUrl } from "@std/path";
import { EverythingRegistry } from "$/common/everything_registry.ts";
import { load_and_check, load_order } from "$/tools/check_mods.ts";
import type { ServerModSource } from "$/server/game/load_mods.ts";
import { GameServer } from "$/server/game/game_server.ts";
import { start_game } from "$/server/game/load_mods.ts";
// mods straight from their source folders, skipping the build: scripts are imported as typescript
export function mod_sources(mods_dir: string): ServerModSource[] {
const mods = load_and_check(mods_dir);
const errors = mods.flatMap((mod) => mod.report.errors.map((e) => `${mod.id}: ${e}`));
if (errors.length) throw new Error(errors.join("\n"));
return load_order(mods).map((mod) => {
const scripts = (mod.manifest?.scripts ?? {}) as Record<string, string>;
const url = (path?: string) => path ? toFileUrl(resolve(mod.dir, path)).href : undefined;
return {
listing: {
id: mod.id,
name: String(mod.manifest?.name),
version: String(mod.manifest?.version),
hash: "source",
data: `mods/${mod.id}/source/data.json`,
},
data: {
blocks: mod.blocks.map((b) => b.json),
items: mod.items.map((i) => i.json),
recipes: mod.recipes.map((r) => r.json),
ores: mod.ores.map((o) => o.json),
},
server_url: url(scripts.server),
worldgen_url: url(scripts.worldgen),
};
});
}
// a game with an in memory host that remembers what it sent to each connection
export async function test_game(mods_dir: string, save?: string, seed = "test-seed") {
EverythingRegistry.clear();
const outbox = new Map<number, unknown[]>();
const host = {
send: (conn: number, data: string) => {
if (!outbox.has(conn)) outbox.set(conn, []);
outbox.get(conn)!.push(JSON.parse(data));
},
close() {},
};
const game: GameServer = await start_game(host, save, seed, mod_sources(mods_dir));
// deno-lint-ignore no-explicit-any
const take = (conn: number): any[] => {
const messages = outbox.get(conn) ?? [];
outbox.set(conn, []);
return messages;
};
const send = (conn: number, message: unknown) => game.on_message(conn, JSON.stringify(message));
return { game, take, send };
}
export function copy_dir(from: string, to: string) {
if (Deno.statSync(from).isDirectory) {
Deno.mkdirSync(to, { recursive: true });
for (const entry of Deno.readDirSync(from)) copy_dir(`${from}/${entry.name}`, `${to}/${entry.name}`);
} else {
Deno.copyFileSync(from, to);
}
}