From d59a901ae0bfe0574d2511615789685158f18509 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 30 Aug 2025 20:44:50 +0300 Subject: [PATCH 1/4] add user properties methods support items --- src/content/loading/BlockLoader.cpp | 16 ++----------- src/content/loading/ContentLoadingCommons.hpp | 23 +++++++++++++++++++ src/content/loading/ItemLoader.cpp | 4 +++- 3 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 src/content/loading/ContentLoadingCommons.hpp diff --git a/src/content/loading/BlockLoader.cpp b/src/content/loading/BlockLoader.cpp index 65e35a69..bde250a5 100644 --- a/src/content/loading/BlockLoader.cpp +++ b/src/content/loading/BlockLoader.cpp @@ -1,5 +1,6 @@ #define VC_ENABLE_REFLECTION #include "ContentUnitLoader.hpp" +#include "ContentLoadingCommons.hpp" #include "../ContentBuilder.hpp" #include "coders/json.hpp" @@ -87,20 +88,7 @@ template<> void ContentUnitLoader::loadUnit( Block& def, const std::string& name, const io::path& file ) { auto root = io::read_json(file); - if (def.properties == nullptr) { - def.properties = dv::object(); - def.properties["name"] = name; - } - for (auto& [key, value] : root.asObject()) { - auto pos = key.rfind('@'); - if (pos == std::string::npos) { - def.properties[key] = value; - continue; - } - auto field = key.substr(0, pos); - auto suffix = key.substr(pos + 1); - process_method(def.properties, suffix, field, value); - } + process_properties(def, name, root); if (root.has("parent")) { const auto& parentName = root["parent"].asString(); diff --git a/src/content/loading/ContentLoadingCommons.hpp b/src/content/loading/ContentLoadingCommons.hpp new file mode 100644 index 00000000..652b550f --- /dev/null +++ b/src/content/loading/ContentLoadingCommons.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "data/dv.hpp" + +#include + +template +inline void process_properties(T& def, const std::string& name, const dv::value& root) { + if (def.properties == nullptr) { + def.properties = dv::object(); + def.properties["name"] = name; + } + for (auto& [key, value] : root.asObject()) { + auto pos = key.rfind('@'); + if (pos == std::string::npos) { + def.properties[key] = value; + continue; + } + auto field = key.substr(0, pos); + auto suffix = key.substr(pos + 1); + process_method(def.properties, suffix, field, value); + } +} diff --git a/src/content/loading/ItemLoader.cpp b/src/content/loading/ItemLoader.cpp index 1703b778..97ddd334 100644 --- a/src/content/loading/ItemLoader.cpp +++ b/src/content/loading/ItemLoader.cpp @@ -1,5 +1,6 @@ #define VC_ENABLE_REFLECTION #include "ContentUnitLoader.hpp" +#include "ContentLoadingCommons.hpp" #include "../ContentBuilder.hpp" #include "coders/json.hpp" @@ -12,11 +13,12 @@ static debug::Logger logger("item-content-loader"); + template<> void ContentUnitLoader::loadUnit( ItemDef& def, const std::string& name, const io::path& file ) { auto root = io::read_json(file); - def.properties = root; + process_properties(def, name, root); if (root.has("parent")) { const auto& parentName = root["parent"].asString(); From 8bdf31d7bb2c88332c737c7c7b3bb1d96a1cfd7c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 30 Aug 2025 23:26:07 +0300 Subject: [PATCH 2/4] add block/item tags --- res/content/base/blocks/coal_ore.json | 1 + res/content/base/blocks/water.json | 3 ++- src/content/ContentBuilder.cpp | 3 +++ src/content/ContentBuilder.hpp | 22 +++++++++++++++++++ src/content/loading/BlockLoader.cpp | 1 + src/content/loading/ContentLoadingCommons.hpp | 14 ++++++++++++ src/content/loading/ItemLoader.cpp | 1 + src/items/ItemDef.hpp | 5 +++++ src/voxels/Block.hpp | 4 ++++ 9 files changed, 53 insertions(+), 1 deletion(-) diff --git a/res/content/base/blocks/coal_ore.json b/res/content/base/blocks/coal_ore.json index c73998ec..ae2bd25b 100644 --- a/res/content/base/blocks/coal_ore.json +++ b/res/content/base/blocks/coal_ore.json @@ -1,4 +1,5 @@ { "texture": "coal_ore", + "tags": ["base:ore"], "base:durability": 16.0 } diff --git a/res/content/base/blocks/water.json b/res/content/base/blocks/water.json index f7785044..0c2fa020 100644 --- a/res/content/base/blocks/water.json +++ b/res/content/base/blocks/water.json @@ -7,5 +7,6 @@ "obstacle": false, "selectable": false, "replaceable": true, - "translucent": true + "translucent": true, + "tags": ["base:liquid"] } diff --git a/src/content/ContentBuilder.cpp b/src/content/ContentBuilder.cpp index ff0bb18a..448adc27 100644 --- a/src/content/ContentBuilder.cpp +++ b/src/content/ContentBuilder.cpp @@ -28,6 +28,9 @@ std::unique_ptr ContentBuilder::build() { // Generating runtime info def.rt.id = blockDefsIndices.size(); def.rt.emissive = *reinterpret_cast(def.emission); + for (const auto& tag : def.tags) { + def.rt.tags.push_back(tags.add(tag)); + } if (def.variants) { for (auto& variant : def.variants->variants) { diff --git a/src/content/ContentBuilder.hpp b/src/content/ContentBuilder.hpp index 7d3df1f6..8a938819 100644 --- a/src/content/ContentBuilder.hpp +++ b/src/content/ContentBuilder.hpp @@ -62,6 +62,27 @@ public: } }; +struct TagsIndices { + int nextIndex = 1; + std::unordered_map map; + + int add(const std::string& tag) { + const auto& found = map.find(tag); + if (found != map.end()) { + return found->second; + } + return map[tag] = nextIndex++; + } + + int indexOf(const std::string& tag) { + const auto& found = map.find(tag); + if (found == map.end()) { + return -1; + } + return found->second; + } +}; + class ContentBuilder { UptrsMap blockMaterials; UptrsMap skeletons; @@ -74,6 +95,7 @@ public: ContentUnitBuilder generators {allNames, ContentType::GENERATOR}; ResourceIndicesSet resourceIndices {}; dv::value defaults = nullptr; + TagsIndices tags {}; ~ContentBuilder(); diff --git a/src/content/loading/BlockLoader.cpp b/src/content/loading/BlockLoader.cpp index bde250a5..a8615e3f 100644 --- a/src/content/loading/BlockLoader.cpp +++ b/src/content/loading/BlockLoader.cpp @@ -89,6 +89,7 @@ template<> void ContentUnitLoader::loadUnit( ) { auto root = io::read_json(file); process_properties(def, name, root); + process_tags(def, root); if (root.has("parent")) { const auto& parentName = root["parent"].asString(); diff --git a/src/content/loading/ContentLoadingCommons.hpp b/src/content/loading/ContentLoadingCommons.hpp index 652b550f..a4a81c98 100644 --- a/src/content/loading/ContentLoadingCommons.hpp +++ b/src/content/loading/ContentLoadingCommons.hpp @@ -21,3 +21,17 @@ inline void process_properties(T& def, const std::string& name, const dv::value& process_method(def.properties, suffix, field, value); } } + +template +inline void process_tags(T& def, const dv::value& root) { + if (!root.has("tags")) { + return; + } + const auto& tags = root["tags"]; + for (const auto& tagValue : tags) { + if (!tagValue.isString()) { + continue; + } + def.tags.push_back(tagValue.asString()); + } +} diff --git a/src/content/loading/ItemLoader.cpp b/src/content/loading/ItemLoader.cpp index 97ddd334..cba07771 100644 --- a/src/content/loading/ItemLoader.cpp +++ b/src/content/loading/ItemLoader.cpp @@ -19,6 +19,7 @@ template<> void ContentUnitLoader::loadUnit( ) { auto root = io::read_json(file); process_properties(def, name, root); + process_tags(def, root); if (root.has("parent")) { const auto& parentName = root["parent"].asString(); diff --git a/src/items/ItemDef.hpp b/src/items/ItemDef.hpp index 4e7e120c..52ebd835 100644 --- a/src/items/ItemDef.hpp +++ b/src/items/ItemDef.hpp @@ -2,6 +2,7 @@ #include #include +#include #include "data/dv.hpp" #include "typedefs.hpp" @@ -64,11 +65,15 @@ struct ItemDef { std::string scriptFile; + std::vector tags; + struct { itemid_t id; blockid_t placingBlock; ItemFuncsSet funcsset {}; bool emissive = false; + + std::vector tags; } rt {}; ItemDef(const std::string& name); diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index 875d6bf4..3d5a86a9 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -261,6 +261,8 @@ public: std::unique_ptr variants; + std::vector tags; + /// @brief Runtime indices (content indexing results) struct { /// @brief block runtime integer id @@ -285,6 +287,8 @@ public: itemid_t pickingItem = 0; blockid_t surfaceReplacement = 0; + + std::vector tags; } rt {}; Block(const std::string& name); From 185b6cc661481d86bd06a2ecc1a75d6a34280dfd Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 30 Aug 2025 23:46:45 +0300 Subject: [PATCH 3/4] add block.has_tag, item.has_tag --- src/content/Content.cpp | 4 +++- src/content/Content.hpp | 12 +++++++++++- src/content/ContentBuilder.cpp | 5 +++-- src/items/ItemDef.hpp | 3 ++- src/logic/scripting/lua/libs/libblock.cpp | 10 ++++++++++ src/logic/scripting/lua/libs/libitem.cpp | 10 ++++++++++ src/voxels/Block.hpp | 3 ++- 7 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/content/Content.cpp b/src/content/Content.cpp index 5ee68859..c0093491 100644 --- a/src/content/Content.cpp +++ b/src/content/Content.cpp @@ -35,13 +35,15 @@ Content::Content( UptrsMap blockMaterials, UptrsMap skeletons, ResourceIndicesSet resourceIndices, - dv::value defaults + dv::value defaults, + std::unordered_map tags ) : indices(std::move(indices)), packs(std::move(packs)), blockMaterials(std::move(blockMaterials)), skeletons(std::move(skeletons)), defaults(std::move(defaults)), + tags(std::move(tags)), blocks(std::move(blocks)), items(std::move(items)), entities(std::move(entities)), diff --git a/src/content/Content.hpp b/src/content/Content.hpp index 81e816b6..599d50fe 100644 --- a/src/content/Content.hpp +++ b/src/content/Content.hpp @@ -176,6 +176,7 @@ class Content { UptrsMap blockMaterials; UptrsMap skeletons; dv::value defaults = nullptr; + std::unordered_map tags; public: ContentUnitDefs blocks; ContentUnitDefs items; @@ -195,7 +196,8 @@ public: UptrsMap blockMaterials, UptrsMap skeletons, ResourceIndicesSet resourceIndices, - dv::value defaults + dv::value defaults, + std::unordered_map tags ); ~Content(); @@ -211,6 +213,14 @@ public: return defaults; } + int getTagIndex(const std::string& tag) const { + const auto& found = tags.find(tag); + if (found == tags.end()) { + return -1; + } + return found->second; + } + const rigging::SkeletonConfig* getSkeleton(const std::string& id) const; const rigging::SkeletonConfig& requireSkeleton(const std::string& id) const; const BlockMaterial* findBlockMaterial(const std::string& id) const; diff --git a/src/content/ContentBuilder.cpp b/src/content/ContentBuilder.cpp index 448adc27..84f2e688 100644 --- a/src/content/ContentBuilder.cpp +++ b/src/content/ContentBuilder.cpp @@ -29,7 +29,7 @@ std::unique_ptr ContentBuilder::build() { def.rt.id = blockDefsIndices.size(); def.rt.emissive = *reinterpret_cast(def.emission); for (const auto& tag : def.tags) { - def.rt.tags.push_back(tags.add(tag)); + def.rt.tags.insert(tags.add(tag)); } if (def.variants) { @@ -96,7 +96,8 @@ std::unique_ptr ContentBuilder::build() { std::move(blockMaterials), std::move(skeletons), std::move(resourceIndices), - std::move(defaults) + std::move(defaults), + std::move(tags.map) ); // Now, it's time to resolve foreign keys diff --git a/src/items/ItemDef.hpp b/src/items/ItemDef.hpp index 52ebd835..40e052b9 100644 --- a/src/items/ItemDef.hpp +++ b/src/items/ItemDef.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "data/dv.hpp" #include "typedefs.hpp" @@ -73,7 +74,7 @@ struct ItemDef { ItemFuncsSet funcsset {}; bool emissive = false; - std::vector tags; + std::set tags; } rt {}; ItemDef(const std::string& name); diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 52e2931c..fd0496fe 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -698,6 +698,15 @@ static int l_reload_script(lua::State* L) { return 0; } +static int l_has_tag(lua::State* L) { + if (auto def = require_block(L)) { + auto tag = lua::require_string(L, 2); + const auto& tags = def->rt.tags; + return lua::pushboolean(L, tags.find(content->getTagIndex(tag)) != tags.end()); + } + return 0; +} + const luaL_Reg blocklib[] = { {"index", lua::wrap}, {"name", lua::wrap}, @@ -737,5 +746,6 @@ const luaL_Reg blocklib[] = { {"get_field", lua::wrap}, {"set_field", lua::wrap}, {"reload_script", lua::wrap}, + {"has_tag", lua::wrap}, {NULL, NULL} }; diff --git a/src/logic/scripting/lua/libs/libitem.cpp b/src/logic/scripting/lua/libs/libitem.cpp index d70a7211..b26e524c 100644 --- a/src/logic/scripting/lua/libs/libitem.cpp +++ b/src/logic/scripting/lua/libs/libitem.cpp @@ -108,6 +108,15 @@ static int l_reload_script(lua::State* L) { return 0; } +static int l_has_tag(lua::State* L) { + if (auto def = get_item_def(L, 1)) { + auto tag = lua::require_string(L, 2); + const auto& tags = def->rt.tags; + return lua::pushboolean(L, tags.find(content->getTagIndex(tag)) != tags.end()); + } + return 0; +} + const luaL_Reg itemlib[] = { {"index", lua::wrap}, {"name", lua::wrap}, @@ -121,5 +130,6 @@ const luaL_Reg itemlib[] = { {"emission", lua::wrap}, {"uses", lua::wrap}, {"reload_script", lua::wrap}, + {"has_tag", lua::wrap}, {NULL, NULL} }; diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index 3d5a86a9..707f6b8b 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "data/dv.hpp" #include "maths/UVRegion.hpp" @@ -288,7 +289,7 @@ public: blockid_t surfaceReplacement = 0; - std::vector tags; + std::set tags; } rt {}; Block(const std::string& name); From 19acda1a88b29a4387ed80409901455481744a10 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 31 Aug 2025 12:55:02 +0300 Subject: [PATCH 4/4] add cached item.has_tag, block.has_tag & update docs --- doc/en/scripting/builtins/libblock.md | 3 ++ doc/en/scripting/builtins/libitem.md | 3 ++ doc/ru/scripting/builtins/libblock.md | 3 ++ doc/ru/scripting/builtins/libitem.md | 3 ++ res/scripts/post_content.lua | 42 +++++++++++++++++++-------- src/content/ContentBuilder.cpp | 2 +- 6 files changed, 43 insertions(+), 13 deletions(-) diff --git a/doc/en/scripting/builtins/libblock.md b/doc/en/scripting/builtins/libblock.md index 8d3b07f8..044555c4 100644 --- a/doc/en/scripting/builtins/libblock.md +++ b/doc/en/scripting/builtins/libblock.md @@ -68,6 +68,9 @@ block.get_variant(x: int, y: int, z: int) -> int -- Sets the block variant by index block.set_variant(x: int, y: int, z: int, index: int) -> int + +-- Checks if an block has specified tag +block.has_tag(id: int, tag: str) -> bool ``` ## Rotation diff --git a/doc/en/scripting/builtins/libitem.md b/doc/en/scripting/builtins/libitem.md index 25a7e48f..d8b42f5c 100644 --- a/doc/en/scripting/builtins/libitem.md +++ b/doc/en/scripting/builtins/libitem.md @@ -33,4 +33,7 @@ item.emission(itemid: int) -> str -- Returns the value of the `uses` property item.uses(itemid: int) -> int + +-- Checks if an item has specified tag +item.has_tag(itemid: int, tag: str) -> bool ``` diff --git a/doc/ru/scripting/builtins/libblock.md b/doc/ru/scripting/builtins/libblock.md index 67547909..1466d1c8 100644 --- a/doc/ru/scripting/builtins/libblock.md +++ b/doc/ru/scripting/builtins/libblock.md @@ -67,6 +67,9 @@ block.get_variant(x: int, y: int, z: int) -> int -- Устанавливает вариант блока по индексу block.set_variant(x: int, y: int, z: int, index: int) -> int + +-- Проверяет наличие тега у блока +block.has_tag(id: int, tag: str) -> bool ``` ### Raycast diff --git a/doc/ru/scripting/builtins/libitem.md b/doc/ru/scripting/builtins/libitem.md index 265bf099..2c312dc4 100644 --- a/doc/ru/scripting/builtins/libitem.md +++ b/doc/ru/scripting/builtins/libitem.md @@ -33,6 +33,9 @@ item.emission(itemid: int) -> str -- Возвращает значение свойства `uses` item.uses(itemid: int) -> int + +-- Проверяет наличие тега у предмета +item.has_tag(itemid: int, tag: str) -> bool ``` diff --git a/res/scripts/post_content.lua b/res/scripts/post_content.lua index 2ad3633b..c8fffc6e 100644 --- a/res/scripts/post_content.lua +++ b/res/scripts/post_content.lua @@ -12,22 +12,31 @@ local names = { for name, _ in pairs(user_props) do table.insert(names, name) end --- remove undefined properties -for id, blockprops in pairs(block.properties) do - for propname, value in pairs(blockprops) do - if not table.has(names, propname) then - blockprops[propname] = nil - end - end -end -for id, itemprops in pairs(item.properties) do - for propname, value in pairs(itemprops) do - if not table.has(names, propname) then - itemprops[propname] = nil + +-- remove undefined properties and build tags set +local function process_properties(properties) + for id, props in pairs(properties) do + local tags_set = nil + for propname, value in pairs(props) do + if propname == "tags" then + if #value > 0 then + tags_set = tags_set or {} + end + for _, tag in ipairs(value) do + tags_set[tag] = true + end + end + if not table.has(names, propname) then + props[propname] = nil + end end + props.tags_set = tags_set end end +process_properties(block.properties) +process_properties(item.properties) + local function make_read_only(t) setmetatable(t, { __newindex = function() @@ -57,6 +66,15 @@ local function cache_names(library) function library.index(name) return indices[name] end + + function library.has_tag(id, tag) + local tags_set = library.properties[id].tags_set + if tags_set then + return tags_set[tag] + else + return false + end + end end cache_names(block) diff --git a/src/content/ContentBuilder.cpp b/src/content/ContentBuilder.cpp index 84f2e688..66ad1acf 100644 --- a/src/content/ContentBuilder.cpp +++ b/src/content/ContentBuilder.cpp @@ -61,7 +61,7 @@ std::unique_ptr ContentBuilder::build() { } blockDefsIndices.push_back(&def); - groups->insert(def.defaults.drawGroup); // FIXME + groups->insert(def.defaults.drawGroup); // FIXME: variants } std::vector itemDefsIndices;