Basic crops system

This commit is contained in:
2026-09-25 22:49:27 -03:00
parent ef25e66f08
commit 099811670f
22 changed files with 985 additions and 178 deletions
+32 -5
View File
@@ -9,9 +9,11 @@ import {
validate_block,
validate_item,
validate_manifest,
validate_model,
validate_ore,
validate_recipe,
} from "$/common/mod_data.ts";
import { BUILTIN_MODELS, type ModelJson } from "$/common/block_models.ts";
export interface ModReport {
id: string;
@@ -25,6 +27,7 @@ export interface LoadedMod {
report: ModReport;
manifest?: Record<string, unknown>;
blocks: { file: string; json: BlockJson }[];
models: { file: string; json: ModelJson }[];
items: { file: string; json: ItemJson }[];
recipes: { file: string; json: RecipeJson }[];
ores: { file: string; json: OreJson }[];
@@ -99,6 +102,7 @@ function load_mod(mods_dir: string, id: string): LoadedMod {
dir,
report: { id, errors: [], warnings: [] },
blocks: [],
models: [],
items: [],
recipes: [],
ores: [],
@@ -147,6 +151,7 @@ function load_mod(mods_dir: string, id: string): LoadedMod {
}
};
load_data("blocks", "block", validate_block, mod.blocks);
load_data("models", "model", validate_model, mod.models);
load_data("items", "item", validate_item, mod.items);
load_data("recipes", "recipe", validate_recipe, mod.recipes);
@@ -164,7 +169,7 @@ function load_mod(mods_dir: string, id: string): LoadedMod {
}
// a mod only registers ids in its own namespace
for (const { file, json } of [...mod.blocks, ...mod.items]) {
for (const { file, json } of [...mod.blocks, ...mod.models, ...mod.items]) {
if (json.id.split(":")[0] !== id) error(`${file}: ${json.id} isn't in this mod's namespace "${id}"`);
}
@@ -186,6 +191,7 @@ function load_mod(mods_dir: string, id: string): LoadedMod {
function check_references(mods: LoadedMod[]) {
const block_owners = new Map<string, string[]>();
const item_owners = new Map<string, string[]>();
const model_owners = new Map<string, string[]>();
const add = (map: Map<string, string[]>, id: string, mod: string) => map.set(id, [...(map.get(id) ?? []), mod]);
const textures = new Set<string>();
@@ -199,6 +205,7 @@ function check_references(mods: LoadedMod[]) {
if (json.item !== false) add(item_owners, json.id, mod.id);
}
for (const { json } of mod.items) add(item_owners, json.id, mod.id);
for (const { json } of mod.models) add(model_owners, json.id, mod.id);
for (const texture of mod.textures) textures.add(texture);
}
@@ -213,12 +220,14 @@ function check_references(mods: LoadedMod[]) {
if (!mod_ids.has(dep)) errors.push(`manifest.json: depends on "${dep}", which isn't installed`);
}
const uses = (file: string, id: string, what: "block" | "item" | "texture") => {
const uses = (file: string, id: string, what: "block" | "item" | "texture" | "model") => {
const namespace = id.split(":")[0];
if (namespace !== mod.id && namespace !== "engine" && !dependencies.has(namespace)) {
warnings.push(`${file}: uses ${id} but doesn't list "${namespace}" in dependencies`);
}
if (what === "texture") {
if (what === "model") {
if (!BUILTIN_MODELS[id] && !model_owners.has(id)) errors.push(`${file}: model ${id} doesn't exist`);
} else if (what === "texture") {
if (!textures.has(id)) warnings.push(`${file}: texture ${id} doesn't exist, it will show as missing`);
} else if (!(what === "block" ? block_owners : item_owners).has(id)) {
errors.push(`${file}: ${what} ${id} doesn't exist`);
@@ -229,10 +238,28 @@ function check_references(mods: LoadedMod[]) {
if ((block_owners.get(json.id)?.length ?? 0) > 1) {
errors.push(`${file}: block ${json.id} is also defined by ${block_owners.get(json.id)!.join(", ")}`);
}
const texture_ids = typeof json.textures === "string" ? [json.textures] : Object.values(json.textures);
for (const texture of texture_ids) uses(file, texture, "texture");
for (const variant of [json, ...Object.values(json.variants ?? {})]) {
if (variant.model) uses(file, variant.model, "model");
const textures = variant.textures ?? {};
for (const texture of typeof textures === "string" ? [textures] : Object.values(textures)) {
uses(file, texture, "texture");
}
}
if (json.drops) uses(file, json.drops, "item");
}
for (const { file, json } of mod.models) {
if ((model_owners.get(json.id)?.length ?? 0) > 1 || BUILTIN_MODELS[json.id]) {
errors.push(`${file}: model ${json.id} is defined more than once`);
}
for (const texture of Object.values(json.textures ?? {})) {
if (!texture.startsWith("#")) uses(file, texture, "texture");
}
for (const element of json.elements) {
for (const face of Object.values(element.faces)) {
if (face && !face.texture.startsWith("#")) uses(file, face.texture, "texture");
}
}
}
for (const { file, json } of mod.items) {
if ((item_owners.get(json.id)?.length ?? 0) > 1) {
errors.push(`${file}: item ${json.id} is also defined by ${item_owners.get(json.id)!.join(", ")}`);