// registers mods' data, the same way on the server and on clients import { BlockRegistry, EverythingRegistry, ItemRegistry } from "./everything_registry.ts"; import { block_from_json, BlockJson, grid_recipe_from_json, GridRecipe, item_from_json, ItemJson, OreJson, RecipeJson, } from "./mod_data.ts"; import { register_block_item } from "./utils.ts"; // everything a mod's json files hold, merged into one file by the build (build/mods///data.json) export interface ModData { blocks: BlockJson[]; items: ItemJson[]; recipes: RecipeJson[]; ores: OreJson[]; } // a mod as the server lists it to clients, in load order. paths are relative to the server's root export interface ModListing { id: string; name: string; version: string; // short hash of everything public, the folder it's served from hash: string; data: string; client?: string; worldgen?: string; // the manifest's credits file, markdown, shown on the credits screen credits?: string; // sha-256 in hex of each file, clients check these before using them sha256: { data: string; client?: string; worldgen?: string; credits?: string }; } // the texture atlas with every mod's textures, built by the server export interface AtlasListing { png: string; json: string; sha256: { png: string; json: string }; } export class RecipeBook { shaped: GridRecipe[] = []; furnace = new Map(); fuel = new Map(); ores: OreJson[] = []; } export class ModLoadError extends Error { constructor(mod: string, message: string) { super(`${mod}: ${message}`); this.name = "ModLoadError"; } } // registers every mod's blocks, items and recipes, in the order given (dependencies first) export function register_mod_data(mods: { id: string; data: ModData }[]): RecipeBook { const recipes = new RecipeBook(); for (const { id, data } of mods) { const fail = (message: string): never => { throw new ModLoadError(id, message); }; try { for (const json of data.blocks) { const { block, has_item } = block_from_json(json); EverythingRegistry.register("blocks", block.id, block); if (has_item) { register_block_item(block); } } for (const json of data.items) { EverythingRegistry.register("items", json.id, item_from_json(json)); } } catch (e) { fail((e as Error).message); } for (const recipe of data.recipes) { switch (recipe.type) { case "shaped": recipes.shaped.push(grid_recipe_from_json(recipe)); break; case "furnace": if (recipes.furnace.has(recipe.input)) fail(`two furnace recipes for ${recipe.input}`); recipes.furnace.set(recipe.input, { output: recipe.output, cook_time: recipe.cook_time }); break; case "fuel": recipes.fuel.set(recipe.item, recipe.burn_time); break; } } recipes.ores.push(...data.ores); } return recipes; }