feat: add resolveItemId and resolveBlockId to DataPack

This commit is contained in:
dualfroz committed 2026-09-07 11:17:59 +02:00
1 parent 570172c86d
commit 4423e6c2b3
1 file changed
+159
+159
View File
@@ -157,3 +157,162 @@ bool readCountRange(const JsonValue& owner, const std::string& key, int& minOut,
} // namespace
ResourceId::ResourceId(std::string space, std::string path)
: space(std::move(space)), path(std::move(path)) {}
bool ResourceId::validSpace(const std::string& text) {
if (text.empty()) return false;
for (char c : text) {
bool ok = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '-' ||
c == '.';
if (!ok) return false;
}
return true;
}
bool ResourceId::validPath(const std::string& text) {
if (text.empty()) return false;
if (text.front() == '/' || text.back() == '/') return false;
for (char c : text) {
bool ok = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '-' ||
c == '.' || c == '/';
if (!ok) return false;
}
return text.find("//") == std::string::npos;
}
bool ResourceId::parse(const std::string& text, const std::string& defaultSpace, ResourceId& out) {
std::size_t colon = text.find(':');
if (colon == std::string::npos) {
out = ResourceId(defaultSpace, text);
} else {
if (text.find(':', colon + 1u) != std::string::npos) return false;
out = ResourceId(text.substr(0, colon), text.substr(colon + 1u));
}
return out.valid();
}
bool resolveItemId(const ResourceId& id, ItemId& out) {
if (!id.valid()) return false;
return parseItemName(loweredWithoutSeparators(id.path), out);
}
bool resolveBlockId(const ResourceId& id, Block& out) {
if (!id.valid()) return false;
return parseBlockName(loweredWithoutSeparators(id.path), out);
}
bool DataPackMeta::fromJson(const JsonValue& root, DataPackMeta& out, std::string& problem) {
if (!root.isObject()) {
problem = "pack metadata must be an object";
return false;
}
const JsonValue* name = root.find("name");
if (name == nullptr || !name->isString() || name->stringValue().empty()) {
problem = "\"name\" must be a non empty string";
return false;
}
DataPackMeta parsed;
parsed.name = name->stringValue();
parsed.description = root.stringOr("description", "");
parsed.formatVersion = static_cast<int>(root.intOr("formatVersion", 1));
parsed.priority = static_cast<int>(root.intOr("priority", 0));
parsed.defaultSpace = root.stringOr("defaultSpace", "voxelforge");
if (parsed.formatVersion < 1) {
problem = "\"formatVersion\" must be at least 1";
return false;
}
if (!ResourceId::validSpace(parsed.defaultSpace)) {
problem = "\"defaultSpace\" is not a valid namespace: " + parsed.defaultSpace;
return false;
}
out = parsed;
return true;
}
std::string DataPackError::text() const {
std::string out = pack.empty() ? file : pack + "/" + file;
if (line > 0) out += ":" + std::to_string(line) + ":" + std::to_string(column);
return out + ": " + reason;
}
void DataPack::addFile(const std::string& path, std::string contents) {
files[path] = std::move(contents);
}
bool DataPack::removeFile(const std::string& path) { return files.erase(path) > 0u; }
bool DataPack::hasFile(const std::string& path) const { return files.count(path) > 0u; }
const std::string* DataPack::fileContents(const std::string& path) const {
auto found = files.find(path);
return found == files.end() ? nullptr : &found->second;
}
std::vector<std::string> DataPack::filesUnder(const std::string& prefix) const {
std::vector<std::string> out;
for (const auto& entry : files)
if (entry.first.compare(0, prefix.size(), prefix) == 0) out.push_back(entry.first);
return out;
}
std::vector<std::string> DataPack::allFiles() const {
std::vector<std::string> out;
out.reserve(files.size());
for (const auto& entry : files) out.push_back(entry.first);
return out;
}
ShapedRecipe RecipeData::toShaped() const {
ShapedRecipe out;
out.width = width;
out.height = height;
out.pattern = pattern;
out.allowMirror = allowMirror;
out.result.item = resultItem;
out.result.count = resultCount;
return out;
}
ShapelessRecipe RecipeData::toShapeless() const {
ShapelessRecipe out;
out.ingredients = ingredients;
out.result.item = resultItem;
out.result.count = resultCount;
return out;
}
SmeltingRecipe RecipeData::toSmelting() const {
SmeltingRecipe out;
out.input = smeltInput;
out.smeltSeconds = smeltSeconds;
out.result.item = resultItem;
out.result.count = resultCount;
return out;
}
bool BlockTag::contains(const ResourceId& member) const {
for (const Entry& entry : entries)
if (!entry.reference && entry.id == member) return true;
return false;
}
void TagRegistry::define(const BlockTag& tag) {
auto found = tags.find(tag.id);
if (found == tags.end() || tag.replace) {
tags[tag.id] = tag;
return;
}
BlockTag& existing = found->second;
for (const BlockTag::Entry& entry : tag.entries) {
bool already = false;
for (const BlockTag::Entry& present : existing.entries)
if (present == entry) already = true;
if (!already) existing.entries.push_back(entry);
}
}
void TagRegistry::clear() { tags.clear(); }
bool TagRegistry::has(const ResourceId& id) const { return tags.count(id) > 0u; }