Actually implement mod code into server
This commit is contained in:
@@ -24,6 +24,28 @@ export function setup(ctx: ServerContext) {
|
||||
on_tick() { throw new Error("broken on purpose"); },
|
||||
on_click(block, _params, player) { log("click", [block.id, player.name]); },
|
||||
});
|
||||
// a box that only takes coal, counts how often it's opened, and gives up its items when broken
|
||||
ctx.components.register_block("testmod:coal_box", {
|
||||
on_interact(block, _params, player) {
|
||||
block.data ??= { container: ctx.containers.create(2).id, opened: 0 };
|
||||
block.data.opened += 1;
|
||||
const screen = ctx.ui.open_container(player, {
|
||||
container: ctx.containers.get(block.data.container)!,
|
||||
layout: [{ slot: 0, x: 0, y: 0, filter: (item) => item.id === "bworld:coal" }, { slot: 1, x: 1, y: 0 }],
|
||||
});
|
||||
screen.set_property("opened", block.data.opened);
|
||||
screen.on_close(() => log("closed", screen.open));
|
||||
return true;
|
||||
},
|
||||
on_break(block) {
|
||||
const container = ctx.containers.get(block.data.container)!;
|
||||
for (let slot = 0; slot < container.size; slot++) {
|
||||
const item = container.get(slot);
|
||||
if (item) ctx.world.drop_item(block.x, block.y, block.z, item);
|
||||
}
|
||||
ctx.containers.delete(container.id);
|
||||
},
|
||||
});
|
||||
ctx.components.register_item("testmod:wand", {
|
||||
on_use(item, params: { power: number }, player) { log("use", [item.id, params.power, player.name]); },
|
||||
});
|
||||
@@ -84,6 +106,10 @@ function test_mods() {
|
||||
`${mod}/blocks/broken.json`,
|
||||
block("testmod:broken", { components: { "testmod:broken": {} } }),
|
||||
);
|
||||
Deno.writeTextFileSync(
|
||||
`${mod}/blocks/coal_box.json`,
|
||||
block("testmod:coal_box", { interactive: true, components: { "testmod:coal_box": {} } }),
|
||||
);
|
||||
Deno.writeTextFileSync(
|
||||
`${mod}/blocks/lamp.json`,
|
||||
block("testmod:lamp", {
|
||||
@@ -227,3 +253,50 @@ Deno.test("/tps reports the loop, and mods can't take engine commands", async ()
|
||||
assert(error.includes("/tps belongs to the engine"), error);
|
||||
Deno.removeSync(clash, { recursive: true });
|
||||
});
|
||||
|
||||
Deno.test("container screens: filters, properties, closing, saving, and dropping what's inside", async () => {
|
||||
const { game, send, take, log, dir } = await setup();
|
||||
game.set_block(1, 100, 0, "testmod:coal_box");
|
||||
send(1, { type: "chat", text: "/give bworld:coal 3" });
|
||||
send(1, { type: "chat", text: "/give bworld:stone 1" });
|
||||
take(1);
|
||||
|
||||
send(1, { type: "use_block", x: 1, y: 100, z: 0, face: "top" });
|
||||
let messages = take(1);
|
||||
const opened = messages.find((m) => m.type === "open_screen");
|
||||
assertEquals(opened.layout, { rows: 1, slots: [{ index: 0, x: 0, y: 0 }, { index: 1, x: 1, y: 0 }], bars: [] });
|
||||
assertEquals(messages.find((m) => m.type === "screen_properties").properties, { opened: 1 });
|
||||
|
||||
// stone doesn't go into the coal slot, but does into the other one. coal goes in
|
||||
const screen = (messages: { type: string; container?: string; items?: unknown[] }[]) =>
|
||||
messages.filter((m) => m.type === "container" && m.container === "screen").at(-1)?.items;
|
||||
send(1, { type: "click", container: "inventory", index: 1, button: 0 });
|
||||
send(1, { type: "click", container: "screen", index: 0, button: 0 });
|
||||
send(1, { type: "click", container: "screen", index: 1, button: 0 });
|
||||
send(1, { type: "click", container: "inventory", index: 0, button: 0 });
|
||||
send(1, { type: "click", container: "screen", index: 0, button: 0 });
|
||||
messages = take(1);
|
||||
assertEquals(screen(messages), [{ id: "bworld:coal", count: 3 }, { id: "bworld:stone", count: 1 }]);
|
||||
|
||||
send(1, { type: "close_screen" });
|
||||
assertEquals(log().closed, [false]);
|
||||
|
||||
// the items and the block's data are saved
|
||||
const loaded = await test_game(dir, game.save());
|
||||
loaded.join(1, "alice");
|
||||
loaded.send(1, { type: "move", x: 0.5, y: 100, z: 0.5, yaw: 0, pitch: 0 });
|
||||
loaded.send(1, { type: "use_block", x: 1, y: 100, z: 0, face: "top" });
|
||||
messages = loaded.take(1);
|
||||
assertEquals(screen(messages), [{ id: "bworld:coal", count: 3 }, { id: "bworld:stone", count: 1 }]);
|
||||
assertEquals(messages.find((m) => m.type === "screen_properties").properties, { opened: 2 });
|
||||
|
||||
// breaking it closes the screen, runs on_close, and drops what was inside
|
||||
loaded.send(1, { type: "break_block", x: 1, y: 100, z: 0 });
|
||||
assert(loaded.take(1).some((m) => m.type === "close_screen"));
|
||||
const logged = loaded.game.mods.storage.testmod.log as Record<string, unknown[]>;
|
||||
assertEquals(logged.closed, [false, false]);
|
||||
const dropped = loaded.game.item_entities().map((e) => e.item.to_data().id).sort();
|
||||
assertEquals(dropped, ["bworld:coal", "bworld:stone"]);
|
||||
assertEquals(loaded.game.containers.size, 0);
|
||||
Deno.removeSync(dir, { recursive: true });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user