From db3b841aec6a09562d8d31753023a797f0bb3980 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Mon, 7 Sep 2026 13:27:21 +0200 Subject: [PATCH] feat: extend DataPack --- core/src/DataPack.cpp | 184 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/core/src/DataPack.cpp b/core/src/DataPack.cpp index fe10d71..3f9f68d 100644 --- a/core/src/DataPack.cpp +++ b/core/src/DataPack.cpp @@ -634,3 +634,187 @@ void DataPackLoader::clear() { itemTagSet.clear(); } +void DataPackLoader::reportError(const std::string& pack, const std::string& file, + const std::string& reason) { + DataPackError failure; + failure.pack = pack; + failure.file = file; + failure.reason = reason; + problems.push_back(failure); +} + +bool DataPackLoader::load() { + problems.clear(); + order.clear(); + folders.clear(); + recipeSet.clear(); + lootSet.clear(); + blockTagSet.clear(); + itemTagSet.clear(); + + std::vector sequence(loaded.size()); + for (std::size_t i = 0u; i < loaded.size(); ++i) sequence[i] = i; + std::stable_sort(sequence.begin(), sequence.end(), [this](std::size_t a, std::size_t b) { + return loaded[a].meta().priority < loaded[b].meta().priority; + }); + + for (std::size_t index : sequence) { + loadPack(loaded[index]); + order.push_back(loaded[index].meta().name); + } + return problems.empty(); +} + +void DataPackLoader::loadPack(DataPack& pack) { + if (const std::string* metaText = pack.fileContents("pack.json")) { + JsonParseResult parsed = parseJson(*metaText); + if (!parsed.ok) { + DataPackError failure; + failure.pack = pack.meta().name; + failure.file = "pack.json"; + failure.reason = parsed.error.message; + failure.line = parsed.error.line; + failure.column = parsed.error.column; + problems.push_back(failure); + } else { + DataPackMeta meta; + std::string problem; + if (DataPackMeta::fromJson(parsed.root, meta, problem)) { + int keptPriority = pack.meta().priority; + pack.meta() = meta; + if (parsed.root.find("priority") == nullptr) pack.meta().priority = keptPriority; + } else { + reportError(pack.meta().name, "pack.json", problem); + } + } + } + + for (const std::string& file : pack.filesUnder("data/")) { + if (!endsWith(file, ".json")) continue; + std::string space; + std::string folder; + std::string name; + if (!splitDataPath(file, space, folder, name)) { + reportError(pack.meta().name, file, + "path does not match data///.json with a known folder"); + continue; + } + ResourceId id(space, name); + if (!id.valid()) { + reportError(pack.meta().name, file, "not a valid resource id: " + id.text()); + continue; + } + JsonParseResult parsed = parseJson(*pack.fileContents(file)); + if (!parsed.ok) { + DataPackError failure; + failure.pack = pack.meta().name; + failure.file = file; + failure.reason = parsed.error.message; + failure.line = parsed.error.line; + failure.column = parsed.error.column; + problems.push_back(failure); + continue; + } + ingest(pack, file, folder, id, parsed.root); + } +} + +void DataPackLoader::ingest(DataPack& pack, const std::string& file, const std::string& folder, + const ResourceId& id, const JsonValue& root) { + folders[folder][id] = root; + const std::string& defaultSpace = pack.meta().defaultSpace; + std::string problem; + if (folder == "recipes") { + RecipeData recipe; + if (!parseRecipeJson(root, defaultSpace, recipe, problem)) { + reportError(pack.meta().name, file, problem); + return; + } + recipe.id = id; + recipeSet[id] = recipe; + return; + } + if (folder == "lootTables") { + LootTable table; + if (!parseLootTableJson(root, defaultSpace, table, problem)) { + reportError(pack.meta().name, file, problem); + return; + } + lootSet[id] = table; + return; + } + if (folder == "tags/blocks" || folder == "tags/items") { + BlockTag tag; + if (!parseTagJson(root, defaultSpace, tag, problem)) { + reportError(pack.meta().name, file, problem); + return; + } + tag.id = id; + if (folder == "tags/blocks") { + blockTagSet.define(tag); + } else { + itemTagSet.define(tag); + } + } +} + +std::vector DataPackLoader::applyOrder() const { return order; } + +const RecipeData* DataPackLoader::recipe(const ResourceId& id) const { + auto found = recipeSet.find(id); + return found == recipeSet.end() ? nullptr : &found->second; +} + +const LootTable* DataPackLoader::lootTable(const ResourceId& id) const { + auto found = lootSet.find(id); + return found == lootSet.end() ? nullptr : &found->second; +} + +const std::map* DataPackLoader::documentsIn(const std::string& folder) const { + auto found = folders.find(folder); + return found == folders.end() ? nullptr : &found->second; +} + +int DataPackLoader::applyTo(CraftingRegistry& registry) const { + int added = 0; + for (const auto& entry : recipeSet) { + const RecipeData& recipe = entry.second; + switch (recipe.kind) { + case RecipeData::Kind::Shaped: registry.addShaped(recipe.toShaped()); break; + case RecipeData::Kind::Shapeless: registry.addShapeless(recipe.toShapeless()); break; + case RecipeData::Kind::Smelting: registry.addSmelting(recipe.toSmelting()); break; + } + ++added; + } + return added; +} + +LootRoll DataPackLoader::roll(const ResourceId& table, const LootContext& context) const { + const LootTable* found = lootTable(table); + if (found == nullptr) return LootRoll(); + return rollLootTable(*found, context); +} + +bool DataPackLoader::blocksInTag(const ResourceId& tag, std::vector& out) const { + out.clear(); + std::vector members; + if (!blockTagSet.resolve(tag, members)) return false; + for (const ResourceId& member : members) { + Block block = Block::Air; + if (resolveBlockId(member, block)) out.push_back(block); + } + return true; +} + +bool DataPackLoader::itemsInTag(const ResourceId& tag, std::vector& out) const { + out.clear(); + std::vector members; + if (!itemTagSet.resolve(tag, members)) return false; + for (const ResourceId& member : members) { + ItemId item = ItemId::None; + if (resolveItemId(member, item)) out.push_back(item); + } + return true; +} + +} // namespace vf