#include "vf/Advancement.h" #include #include "vf/Command.h" #include "vf/PotionCatalog.h" namespace vf { namespace { struct NamedDimension { const char* name; DimensionId id; }; // Kept in step with DimensionConfig::nameOf, which Dimension.cpp cannot hand // back a parser for without dragging every world generator into the link. const NamedDimension dimensionNames[] = { {"overworld", DimensionId::Overworld}, {"nether", DimensionId::Nether}, {"end", DimensionId::End}, }; bool parseDimensionName(const std::string& text, DimensionId& out) { for (const NamedDimension& entry : dimensionNames) { if (text == entry.name) { out = entry.id; return true; } } return false; } bool parsePotionName(const std::string& text, PotionKind& out) { const PotionCatalog& catalog = potionCatalog(); for (const PotionEntry& entry : catalog.entries()) { if (text == entry.name) { out = entry.kind; return true; } } return false; } bool parseProfessionName(const std::string& text, VillagerProfession& out) { for (int i = 0; i < static_cast(VillagerProfession::Count); ++i) { VillagerProfession profession = static_cast(i); if (text == villagerProfessionName(profession)) { out = profession; return true; } } return false; } bool readOptionalResource(const JsonValue& owner, const std::string& key, const std::string& defaultSpace, ResourceId& out, bool& present, std::string& problem) { const JsonValue* field = owner.find(key); present = false; if (field == nullptr) return true; if (!field->isString()) { problem = "\"" + key + "\" must be a string"; return false; } if (!ResourceId::parse(field->stringValue(), defaultSpace, out)) { problem = "\"" + key + "\" is not a valid resource id: " + field->stringValue(); return false; } present = true; return true; } bool readResourceList(const JsonValue& owner, const std::string& key, const std::string& defaultSpace, std::vector& out, std::string& problem) { const JsonValue* list = owner.find(key); if (list == nullptr) return true; if (!list->isArray()) { problem = "\"" + key + "\" must be an array of resource ids"; return false; } for (const JsonValue& element : list->elements()) { ResourceId id; if (!element.isString() || !ResourceId::parse(element.stringValue(), defaultSpace, id)) { problem = "\"" + key + "\" holds something that is not a resource id"; return false; } out.push_back(id); } return true; } bool parseCriterionJson(const std::string& criterionName, const JsonValue& root, const std::string& defaultSpace, AdvancementCriterion& out, std::string& problem) { if (!root.isObject()) { problem = "criterion \"" + criterionName + "\" must be an object"; return false; } const JsonValue* trigger = root.find("trigger"); if (trigger == nullptr || !trigger->isString() || !parseAdvancementTriggerName(trigger->stringValue(), out.trigger)) { problem = "criterion \"" + criterionName + "\" needs a known \"trigger\""; return false; } out.name = criterionName; out.requiredCount = static_cast(root.intOr("count", 1)); if (out.requiredCount < 1) { problem = "criterion \"" + criterionName + "\" needs a count of at least 1"; return false; } ResourceId id; bool present = false; if (!readOptionalResource(root, "item", defaultSpace, id, present, problem)) return false; if (present) { if (!resolveItemId(id, out.item)) { problem = "criterion \"" + criterionName + "\": no such item " + id.text(); return false; } out.matchMask |= AdvancementCriterion::MatchItem; } if (!readOptionalResource(root, "block", defaultSpace, id, present, problem)) return false; if (present) { if (!resolveBlockId(id, out.block)) { problem = "criterion \"" + criterionName + "\": no such block " + id.text(); return false; } out.matchMask |= AdvancementCriterion::MatchBlock; } if (!readOptionalResource(root, "tool", defaultSpace, id, present, problem)) return false; if (present) { if (!resolveItemId(id, out.tool)) { problem = "criterion \"" + criterionName + "\": no such tool " + id.text(); return false; } out.matchMask |= AdvancementCriterion::MatchTool; } if (const JsonValue* species = root.find("species")) { if (!species->isString() || !parseSpeciesName(species->stringValue(), out.species)) { problem = "criterion \"" + criterionName + "\": unknown species"; return false; } out.matchMask |= AdvancementCriterion::MatchSpecies; } if (const JsonValue* dimension = root.find("dimension")) { if (!dimension->isString() || !parseDimensionName(dimension->stringValue(), out.dimension)) { problem = "criterion \"" + criterionName + "\": unknown dimension"; return false; } out.matchMask |= AdvancementCriterion::MatchDimension; } if (const JsonValue* potion = root.find("potion")) { if (!potion->isString() || !parsePotionName(potion->stringValue(), out.potion)) { problem = "criterion \"" + criterionName + "\": unknown potion"; return false; } out.matchMask |= AdvancementCriterion::MatchPotion; } if (const JsonValue* profession = root.find("profession")) { if (!profession->isString() || !parseProfessionName(profession->stringValue(), out.profession)) { problem = "criterion \"" + criterionName + "\": unknown villager profession"; return false; } out.matchMask |= AdvancementCriterion::MatchProfession; } return true; }