Appearance
Example: bedwars
From the runnable examples. Lint, build and run it from servers/inventery/:
sh
node plot/sdk/lint.mjs docs/developers/examples/bedwars
node plot/sdk/build.mjs docs/developers/examples/bedwars
node docs/developers/examples/run.mjs docs/developers/examples/bedwarsplot.json
json
{
"kind": "mod",
"id": "ana.bedwars",
"namespace": "bedwars",
"name": "Bedwars",
"version": "2.0.0",
"apiVersion": 1,
"code": "dist/logic.js",
"depends": ["inventery.team-minigame"],
"tags": ["pvp", "teams", "minigame"]
}logic.ts
ts
// Bedwars as a game mod on the Team Minigame runtime.
//
// A plot runs one runtime (how the plot is run) and any number of mods (register #94). This plot's runtime is Team
// Minigame (plot/samples/team-minigame): it runs the round of any team game. Lobbies of N with stashed items, teams
// balanced as players arrive, a full lobby locked with one start countdown, every team on its spawn with the kit,
// a death as a respawn or as out, the last team standing wins, then the round's edits undone and the next countdown.
//
// This mod adds only what makes the round Bedwars:
// - the map (map.json: two sky islands, a middle, a glass perch for the dead, the lobby box)
// - the kit, and each team's bed as its respawn status ("BED" on the scoreboard, "X" once it's gone)
// - generators (iron on each island, gold in the middle) and a shop the runtime shows as buttons
// - which blocks players may break and place
//
// The base game runs inside the plot (#111) and does everything else: beds in two halves, broken whole; placing the
// wool you hold; swords, knockback and armour; items dropped and picked up by walking over them. The runtime and this
// mod only filter it (return false to refuse; any refusal wins) and hear what it did.
import { world, ui } from "@inventery/host";
import type { Vec3 } from "@inventery/host";
import { blocks, events, structures, generators, shop, edits } from "@inventery/minecraft";
import type { Marks } from "@inventery/minecraft";
import { teamGame as game } from "../../../../plot/samples/team-minigame/api";
import MAP from "./map.json";
// The runtime and this mod are separate sandboxed components: they talk through events, as data (mods.sendEvent ->
// onModEvent). api.ts's onModEvent turns the runtime's events into game.onMap, game.onRoundStart and the others.
export { onModEvent } from "../../../../plot/samples/team-minigame/api";
// The base game's block events reach a package through these hooks; events.blockBreak and the others listen to them.
export { onBlockBreak, onBlockPlace, onBlockBroken, onBlockPlaced } from "@inventery/minecraft";
const TEAMS = ["red", "blue"]; // team names are also their colours
const IRON = "inventery:iron_ingot", GOLD = "inventery:gold_ingot";
const AIR = world.blockId("air");
// The shop: one button per offer, paid in items. Only a runtime may put buttons on screen (ui.setButton), so the mod
// hands the runtime SHOP.buttons() and gets the presses back (game.onButton below).
const SHOP = shop([
{ button: "wool", item: (p) => `inventery:${game.teamOf(p)}_wool`, count: 16, price: [IRON, 4], label: "16 WOOL" },
{ button: "stone_sword", item: "inventery:stone_sword", price: [IRON, 10], label: "SWORD 2" },
{ button: "iron_sword", item: "inventery:iron_sword", price: [GOLD, 4], label: "SWORD 3" },
]);
// A broken bed drops nothing here (the base game would drop a bed item). Every facing, both halves.
for (const facing of ["", "_south", "_west", "_east"]) {
for (const half of ["bed", "bed_head"]) world.setDrops(world.blockId(half + facing), []);
}
// ---- 1. Register the game with the runtime (at the top level: every load)
game.register({
name: "Bedwars",
map: MAP, // built by the runtime into the plot's shared base
teams: TEAMS,
kit: [{ item: "inventery:wooden_sword", count: 1 }], // given at every spawn
status: "BED", // each team's scoreboard status when the round starts
buttons: SHOP.buttons(),
goal: "Protect your bed", // the round's opening subtitle
outText: "Your bed is gone", // the subtitle when you're out
});
// ---- 2. Generators and the shop
// The runtime built the map and sends its marks (plot level, once a load): every "iron" and "gold" mark gets a
// generator. The marks are kept to find the beds.
let marks: Marks = {};
game.onMap((m) => {
marks = m;
generators.add({ at: structures.find(m, "iron"), item: IRON, every: 1, cap: 32 });
generators.add({ at: structures.find(m, "gold"), item: GOLD, every: 6, cap: 8 });
});
// A round started in this lobby: everyone is on their team's spawn with the kit. The runtime undid the last round's
// block edits itself, so this mod forgets its own record of them (edits.original starts afresh) and starts dropping.
game.onRoundStart(() => {
edits.forget();
generators.start();
});
game.onRoundEnd(() => generators.stop()); // clears what's still lying there
game.onButton((p, button) => SHOP.buy(p, button));
export function tickLobby() {
generators.tick();
}
// ---- 3. Beds: who respawns
const same = (a: Vec3, b: Vec3) => a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
// Which team's bed a cell is (either half), if any: the map marks both halves "bed" on each team's island.
const bedOf = (pos: Vec3) => TEAMS.find((t) => marks[t]?.bed?.some((c) => same(c, pos)));
// Before the base game breaks a block. The runtime already refuses breaking outside a round and by players who are
// out; this refusal comes on top. In the round you may break the other team's bed and blocks placed this round.
events.blockBreak.on((ev) => {
if (!game.playing(ev.by)) return false;
const bed = bedOf(ev.pos);
if (bed) return bed !== game.teamOf(ev.by); // not your own bed
return edits.original(ev.pos) === AIR; // a bridge, not the islands
});
// After the base game broke a bed (both halves are gone): that team respawns no more.
events.blockBroken.on((ev) => {
const bed = bedOf(ev.pos);
if (!bed) return;
game.setRespawn(bed, false); // canRespawn(team): from now that team is out when it dies
game.setStatus(bed, "X"); // the scoreboard row: "BED 1" becomes "X 1"
ui.showTitle("lobby", `${bed.toUpperCase()} bed destroyed!`, { color: bed });
});
// Before the base game places a block: only team wool, by players in the round.
events.blockPlace.on((ev) => game.playing(ev.by) && blocks.oneOf(ev, TEAMS.map((t) => `${t}_wool`)));