From 5f6ae5daba600ae738f534426665df399daa8afe Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Tue, 20 Aug 2024 21:58:01 +0300 Subject: [PATCH 1/4] ItemDef/EntityDef/Block: Add method `cloneTo` to definition to other definition ContentBuilder: Add method `get` to get definition or nullptr ContentLoader: Add functionality to clone from definition specified in `parent` field in json --- src/content/ContentBuilder.hpp | 8 ++++++ src/content/ContentLoader.cpp | 47 +++++++++++++++++++++++++++++++--- src/content/ContentLoader.hpp | 6 ++--- src/items/ItemDef.cpp | 10 ++++++++ src/items/ItemDef.hpp | 1 + src/objects/EntityDef.cpp | 12 +++++++++ src/objects/EntityDef.hpp | 6 +++-- src/voxels/Block.cpp | 44 ++++++++++++++++++++++++++++--- src/voxels/Block.hpp | 2 ++ 9 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 src/objects/EntityDef.cpp diff --git a/src/content/ContentBuilder.hpp b/src/content/ContentBuilder.hpp index 9207316c..d3e8f536 100644 --- a/src/content/ContentBuilder.hpp +++ b/src/content/ContentBuilder.hpp @@ -44,6 +44,14 @@ public: defs[id] = std::make_unique(id); return *defs[id]; } + // Only fetch existing definition, return null otherwise. + T* get(const std::string& id) { + auto found = defs.find(id); + if (found != defs.end()) { + return &*found->second; + } + return nullptr; + } auto build() { return std::move(defs); diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 26e8f5f0..ba07791b 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -6,6 +6,9 @@ #include #include +#include "Content.hpp" +#include "ContentBuilder.hpp" +#include "ContentPack.hpp" #include "coders/json.hpp" #include "core_defs.hpp" #include "data/dynamic.hpp" @@ -18,9 +21,6 @@ #include "util/listutil.hpp" #include "util/stringutil.hpp" #include "voxels/Block.hpp" -#include "Content.hpp" -#include "ContentBuilder.hpp" -#include "ContentPack.hpp" namespace fs = std::filesystem; @@ -123,6 +123,18 @@ void ContentLoader::loadBlock( ) { auto root = files::read_json(file); + if (root->has("parent")) { + std::string parentName; + root->str("parent", parentName); + auto parentDef = this->builder.blocks.get(parentName); + if (parentDef == nullptr) { + throw std::runtime_error( + "Failed to find parent(" + parentName + ") for " + name + ); + } + parentDef->cloneTo(def); + } + root->str("caption", def.caption); // block texturing @@ -289,6 +301,19 @@ void ContentLoader::loadItem( ItemDef& def, const std::string& name, const fs::path& file ) { auto root = files::read_json(file); + + if (root->has("parent")) { + std::string parentName; + root->str("parent", parentName); + auto parentDef = this->builder.items.get(parentName); + if (parentDef == nullptr) { + throw std::runtime_error( + "Failed to find parent(" + parentName + ") for " + name + ); + } + parentDef->cloneTo(def); + } + root->str("caption", def.caption); std::string iconTypeStr = ""; @@ -319,6 +344,19 @@ void ContentLoader::loadEntity( EntityDef& def, const std::string& name, const fs::path& file ) { auto root = files::read_json(file); + + if (root->has("parent")) { + std::string parentName; + root->str("parent", parentName); + auto parentDef = this->builder.entities.get(parentName); + if (parentDef == nullptr) { + throw std::runtime_error( + "Failed to find parent(" + parentName + ") for " + name + ); + } + parentDef->cloneTo(def); + } + if (auto componentsarr = root->list("components")) { for (size_t i = 0; i < componentsarr->size(); i++) { def.components.emplace_back(componentsarr->str(i)); @@ -340,7 +378,8 @@ void ContentLoader::loadEntity( sensorarr->num(3)}, {sensorarr->num(4), sensorarr->num(5), - sensorarr->num(6)}} + sensorarr->num(6)} + } ); } else if (sensorType == "radius") { def.radialSensors.emplace_back(i, sensorarr->num(1)); diff --git a/src/content/ContentLoader.hpp b/src/content/ContentLoader.hpp index 0673a96d..5087378b 100644 --- a/src/content/ContentLoader.hpp +++ b/src/content/ContentLoader.hpp @@ -42,13 +42,13 @@ class ContentLoader { static void loadCustomBlockModel(Block& def, dynamic::Map* primitives); static void loadBlockMaterial(BlockMaterial& def, const fs::path& file); - static void loadBlock( + void loadBlock( Block& def, const std::string& name, const fs::path& file ); - static void loadItem( + void loadItem( ItemDef& def, const std::string& name, const fs::path& file ); - static void loadEntity( + void loadEntity( EntityDef& def, const std::string& name, const fs::path& file ); void loadResources(ResourceType type, dynamic::List* list); diff --git a/src/items/ItemDef.cpp b/src/items/ItemDef.cpp index 539682c6..321f7be7 100644 --- a/src/items/ItemDef.cpp +++ b/src/items/ItemDef.cpp @@ -5,3 +5,13 @@ ItemDef::ItemDef(const std::string& name) : name(name) { caption = util::id_to_caption(name); } +void ItemDef::cloneTo(ItemDef& dst) { + dst.caption = caption; + dst.stackSize = stackSize; + dst.generated = generated; + std::copy(&emission[0], &emission[3], dst.emission); + dst.iconType = iconType; + dst.icon = icon; + dst.placingBlock = placingBlock; + dst.scriptName = scriptName; +} diff --git a/src/items/ItemDef.hpp b/src/items/ItemDef.hpp index 348ff844..a306dce2 100644 --- a/src/items/ItemDef.hpp +++ b/src/items/ItemDef.hpp @@ -44,4 +44,5 @@ struct ItemDef { ItemDef(const std::string& name); ItemDef(const ItemDef&) = delete; + void cloneTo(ItemDef& dst); }; diff --git a/src/objects/EntityDef.cpp b/src/objects/EntityDef.cpp new file mode 100644 index 00000000..8fa9c129 --- /dev/null +++ b/src/objects/EntityDef.cpp @@ -0,0 +1,12 @@ +#include "EntityDef.hpp" +void EntityDef::cloneTo(EntityDef& dst) { + dst.components = components; + dst.bodyType = bodyType; + dst.hitbox = hitbox; + dst.boxSensors = boxSensors; + dst.radialSensors = radialSensors; + dst.skeletonName = skeletonName; + dst.blocking = blocking; + dst.save = save; + +} \ No newline at end of file diff --git a/src/objects/EntityDef.hpp b/src/objects/EntityDef.hpp index 021c67f1..dec61730 100644 --- a/src/objects/EntityDef.hpp +++ b/src/objects/EntityDef.hpp @@ -24,12 +24,12 @@ struct EntityDef { /// @brief Hitbox size glm::vec3 hitbox {0.25f}; - + /// @brief 'aabb' sensors std::vector> boxSensors {}; /// @brief 'radius' sensors std::vector> radialSensors {}; - + /// @brief Skeleton ID std::string skeletonName = name; @@ -56,4 +56,6 @@ struct EntityDef { EntityDef(const std::string& name) : name(name) {} EntityDef(const EntityDef&) = delete; + void cloneTo(EntityDef& dst); }; + diff --git a/src/voxels/Block.cpp b/src/voxels/Block.cpp index aee744a1..ec125430 100644 --- a/src/voxels/Block.cpp +++ b/src/voxels/Block.cpp @@ -64,7 +64,8 @@ const BlockRotProfile BlockRotProfile::NONE { {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}, // West {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}, // Up {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}, // Down - }}; + } +}; const BlockRotProfile BlockRotProfile::PIPE { "pipe", @@ -75,7 +76,8 @@ const BlockRotProfile BlockRotProfile::PIPE { {{0, 0, -1}, {1, 0, 0}, {0, -1, 0}}, // West {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}, // Up {{1, 0, 0}, {0, -1, 0}, {0, 0, -1}}, // Down - }}; + } +}; const BlockRotProfile BlockRotProfile::PANE { "pane", @@ -84,7 +86,8 @@ const BlockRotProfile BlockRotProfile::PANE { {{0, 0, -1}, {0, 1, 0}, {1, 0, 0}}, // East {{-1, 0, 0}, {0, 1, 0}, {0, 0, -1}}, // South {{0, 0, 1}, {0, 1, 0}, {-1, 0, 0}}, // West - }}; + } +}; Block::Block(const std::string& name) : name(name), @@ -95,10 +98,43 @@ Block::Block(const std::string& name) TEXTURE_NOTFOUND, TEXTURE_NOTFOUND, TEXTURE_NOTFOUND, - TEXTURE_NOTFOUND} { + TEXTURE_NOTFOUND + } { } Block::Block(std::string name, const std::string& texture) : name(std::move(name)), textureFaces {texture, texture, texture, texture, texture, texture} { } +void Block::cloneTo(Block& dst) { + dst.caption = caption; + for (int i = 0; i < 6; i++) { + dst.textureFaces[i] = textureFaces[i]; + } + dst.modelTextures = modelTextures; + dst.modelBoxes = modelBoxes; + dst.modelExtraPoints = modelExtraPoints; + dst.modelUVs = modelUVs; + dst.material = material; + std::copy(&emission[0], &emission[3], dst.emission); + dst.size = size; + dst.model = model; + dst.lightPassing = lightPassing; + dst.skyLightPassing = skyLightPassing; + dst.shadeless = shadeless; + dst.ambientOcclusion = ambientOcclusion; + dst.obstacle = obstacle; + dst.selectable = selectable; + dst.replaceable = replaceable; + dst.breakable = breakable; + dst.rotatable = rotatable; + dst.grounded = grounded; + dst.hidden = hidden; + dst.hitboxes = hitboxes; + dst.rotations = rotations; + dst.pickingItem = pickingItem; + dst.scriptName = scriptName; + dst.uiLayout = uiLayout; + dst.inventorySize = inventorySize; + dst.tickInterval = tickInterval; +} diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index a32ddd1c..210972bb 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -211,6 +211,8 @@ public: Block(const std::string& name); Block(std::string name, const std::string& texture); Block(const Block&) = delete; + + void cloneTo(Block& dst); }; inline glm::ivec3 get_ground_direction(const Block& def, int rotation) { From 16ddd943c20cbae9c480ad2c7ba51661740b13c3 Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Wed, 21 Aug 2024 19:15:09 +0300 Subject: [PATCH 2/4] ItemDef/EntityDef/Block: Add proper dependency resolution for blocks, items, entities --- src/content/ContentLoader.cpp | 165 ++++++++++++++++++++++++++++------ 1 file changed, 137 insertions(+), 28 deletions(-) diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index ba07791b..42fbf7c7 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -480,46 +480,155 @@ void ContentLoader::load() { if (!fs::is_regular_file(pack->getContentFile())) return; auto root = files::read_json(pack->getContentFile()); + std::vector> pendingDefs; + auto getJsonParent = [&](std::string prerix, std::string name) { + auto configFile = + pack->folder / fs::path(prerix + "/" + name + ".json"); + std::string parent; + if (fs::exists(configFile)) { + auto root = files::read_json(configFile); + if (root->has("parent")) root->str("parent", parent); + } + return parent; + }; + auto processName = [&](std::string name) { + auto colon = name.find(':'); + std::string full = + colon == std::string::npos ? pack->id + ":" + name : name; + if (colon != std::string::npos) name[colon] = '/'; + + return std::make_pair(full, name); + }; if (auto blocksarr = root->list("blocks")) { for (size_t i = 0; i < blocksarr->size(); i++) { - std::string name = blocksarr->str(i); - auto colon = name.find(':'); - std::string full = - colon == std::string::npos ? pack->id + ":" + name : name; - if (colon != std::string::npos) name[colon] = '/'; - auto& def = builder.blocks.create(full); - if (colon != std::string::npos) - def.scriptName = name.substr(0, colon) + '/' + def.scriptName; - loadBlock(def, full, name); - stats->totalBlocks++; + auto [full, name] = processName(blocksarr->str(i)); + auto parent = getJsonParent("blocks", name); + if (parent.empty() || builder.blocks.get(parent)) { + // No dependency or dependency already loaded/exists in another + // content pack + auto& def = builder.blocks.create(full); + loadBlock(def, full, name); + stats->totalBlocks++; + } else { + // Dependency not loaded yet, add to pending items + pendingDefs.emplace_back(full, name); + } + } + + // Resolve dependencies for pending items + bool progressMade = true; + while (!pendingDefs.empty() && progressMade) { + progressMade = false; + + for (auto it = pendingDefs.begin(); it != pendingDefs.end();) { + auto parent = getJsonParent("blocks", it->second); + if (builder.blocks.get(parent)) { + // Dependency resolved or parent exists in another pack, + // load the item + auto& def = builder.blocks.create(it->first); + loadBlock(def, it->first, it->second); + stats->totalBlocks++; + it = pendingDefs.erase(it); // Remove resolved item + progressMade = true; + } else { + ++it; + } + } + } + + if (!pendingDefs.empty()) { + // Handle circular dependencies or missing dependencies + // You can log an error or throw an exception here if necessary + throw std::runtime_error("Unresolved block dependencies detected."); } } + if (auto itemsarr = root->list("items")) { for (size_t i = 0; i < itemsarr->size(); i++) { - std::string name = itemsarr->str(i); - auto colon = name.find(':'); - std::string full = - colon == std::string::npos ? pack->id + ":" + name : name; - if (colon != std::string::npos) name[colon] = '/'; - auto& def = builder.items.create(full); - if (colon != std::string::npos) - def.scriptName = name.substr(0, colon) + '/' + def.scriptName; - loadItem(def, full, name); - stats->totalItems++; + auto [full, name] = processName(itemsarr->str(i)); + auto parent = getJsonParent("items", name); + if (parent.empty() || builder.items.get(parent)) { + // No dependency or dependency already loaded/exists in another + // content pack + auto& def = builder.items.create(full); + loadItem(def, full, name); + stats->totalItems++; + } else { + // Dependency not loaded yet, add to pending items + pendingDefs.emplace_back(full, name); + } + } + + // Resolve dependencies for pending items + bool progressMade = true; + while (!pendingDefs.empty() && progressMade) { + progressMade = false; + + for (auto it = pendingDefs.begin(); it != pendingDefs.end();) { + auto parent = getJsonParent("items", it->second); + if (builder.items.get(parent)) { + // Dependency resolved or parent exists in another pack, + // load the item + auto& def = builder.items.create(it->first); + loadItem(def, it->first, it->second); + stats->totalItems++; + it = pendingDefs.erase(it); // Remove resolved item + progressMade = true; + } else { + ++it; + } + } + } + + if (!pendingDefs.empty()) { + // Handle circular dependencies or missing dependencies + // You can log an error or throw an exception here if necessary + throw std::runtime_error("Unresolved item dependencies detected."); } } if (auto entitiesarr = root->list("entities")) { for (size_t i = 0; i < entitiesarr->size(); i++) { - std::string name = entitiesarr->str(i); - auto colon = name.find(':'); - std::string full = - colon == std::string::npos ? pack->id + ":" + name : name; - if (colon != std::string::npos) name[colon] = '/'; - auto& def = builder.entities.create(full); - loadEntity(def, full, name); - stats->totalEntities++; + auto [full, name] = processName(entitiesarr->str(i)); + auto parent = getJsonParent("entities", name); + if (parent.empty() || builder.entities.get(parent)) { + // No dependency or dependency already loaded/exists in another + // content pack + auto& def = builder.entities.create(full); + loadEntity(def, full, name); + stats->totalEntities++; + } else { + // Dependency not loaded yet, add to pending items + pendingDefs.emplace_back(full, name); + } + } + + // Resolve dependencies for pending items + bool progressMade = true; + while (!pendingDefs.empty() && progressMade) { + progressMade = false; + + for (auto it = pendingDefs.begin(); it != pendingDefs.end();) { + auto parent = getJsonParent("entities", it->second); + if (builder.entities.get(parent)) { + // Dependency resolved or parent exists in another pack, + // load the item + auto& def = builder.entities.create(it->first); + loadEntity(def, it->first, it->second); + stats->totalEntities++; + it = pendingDefs.erase(it); // Remove resolved item + progressMade = true; + } else { + ++it; + } + } + } + + if (!pendingDefs.empty()) { + // Handle circular dependencies or missing dependencies + // You can log an error or throw an exception here if necessary + throw std::runtime_error("Unresolved entities dependencies detected."); } } From fcd26f4b10f8c0059385edf556375f63fe4fe168 Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Wed, 21 Aug 2024 19:28:08 +0300 Subject: [PATCH 3/4] ContentLoader::load: Minor cleanup --- src/content/ContentLoader.cpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 42fbf7c7..45abb7f9 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -481,23 +481,23 @@ void ContentLoader::load() { auto root = files::read_json(pack->getContentFile()); std::vector> pendingDefs; - auto getJsonParent = [&](std::string prerix, std::string name) { - auto configFile = - pack->folder / fs::path(prerix + "/" + name + ".json"); - std::string parent; - if (fs::exists(configFile)) { - auto root = files::read_json(configFile); - if (root->has("parent")) root->str("parent", parent); - } - return parent; - }; - auto processName = [&](std::string name) { + auto getJsonParent = [this](const std::string& prefix, const std::string& name) { + auto configFile = pack->folder / fs::path(prefix + "/" + name + ".json"); + std::string parent; + if (fs::exists(configFile)) { + auto root = files::read_json(configFile); + if (root->has("parent")) root->str("parent", parent); + } + return parent; + }; + auto processName = [this](const std::string& name) { auto colon = name.find(':'); + auto new_name = name; std::string full = colon == std::string::npos ? pack->id + ":" + name : name; - if (colon != std::string::npos) name[colon] = '/'; + if (colon != std::string::npos) new_name[colon] = '/'; - return std::make_pair(full, name); + return std::make_pair(full, new_name); }; if (auto blocksarr = root->list("blocks")) { @@ -628,7 +628,9 @@ void ContentLoader::load() { if (!pendingDefs.empty()) { // Handle circular dependencies or missing dependencies // You can log an error or throw an exception here if necessary - throw std::runtime_error("Unresolved entities dependencies detected."); + throw std::runtime_error( + "Unresolved entities dependencies detected." + ); } } From 719135fe19de0b9f96e457b2f3d9293381fa3466 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 22 Aug 2024 23:50:46 +0300 Subject: [PATCH 4/4] fix animation loading --- src/assets/AssetsLoader.cpp | 3 +++ src/assets/assetload_funcs.cpp | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index 9a570fc2..a4853606 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -194,6 +194,9 @@ void AssetsLoader::processPreloadConfigs(const Content* content) { return; } for (auto& entry : content->getPacks()) { + if (entry.first == "core") { + continue; + } const auto& pack = entry.second; auto preloadFile = pack->getInfo().folder / fs::path("preload.json"); if (fs::exists(preloadFile)) { diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index 97107862..35133264 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -12,6 +12,7 @@ #include "coders/obj.hpp" #include "constants.hpp" #include "data/dynamic.hpp" +#include "debug/Logger.hpp" #include "files/engine_paths.hpp" #include "files/files.hpp" #include "frontend/UiDocument.hpp" @@ -26,6 +27,8 @@ #include "Assets.hpp" #include "AssetsLoader.hpp" +static debug::Logger logger("assetload-funcs"); + namespace fs = std::filesystem; static bool animation( @@ -263,7 +266,7 @@ static TextureAnimation create_animation( for (const auto& elem : frameList) { if (!srcAtlas->has(elem.first)) { - std::cerr << "Unknown frame name: " << elem.first << std::endl; + logger.error() << "unknown frame name: " << elem.first; continue; } region = srcAtlas->get(elem.first);