From 95fedfa3a3bf21203413e3e2a87a1d700333f158 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 8 Apr 2024 03:17:44 +0300 Subject: [PATCH] packs manager (unused yet) --- src/content/PacksManager.cpp | 131 +++++++++++++++++++++++++++++++++++ src/content/PacksManager.h | 36 ++++++++++ src/util/listutil.h | 9 ++- 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 src/content/PacksManager.cpp create mode 100644 src/content/PacksManager.h diff --git a/src/content/PacksManager.cpp b/src/content/PacksManager.cpp new file mode 100644 index 00000000..6ff3a020 --- /dev/null +++ b/src/content/PacksManager.cpp @@ -0,0 +1,131 @@ +#include "PacksManager.h" + +#include "../util/listutil.h" + +#include +#include + +PacksManager::PacksManager() { +} + +void PacksManager::setSources(std::vector sources) { + this->sources = sources; +} + +void PacksManager::scan() { + packs.clear(); + + std::vector packsList; + for (auto& folder : sources) { + ContentPack::scanFolder(folder, packsList); + for (auto& pack : packsList) { + packs.emplace(pack.id, pack); + } + } +} + +std::vector PacksManager::getAllNames() { + std::vector names; + for (auto& entry : packs) { + names.push_back(entry.first); + } + return names; +} + +static contentpack_error on_circular_dependency(std::queue& queue) { + ContentPack* lastPack = queue.back(); + // circular dependency + std::stringstream ss; + ss << "circular dependency: " << lastPack->id; + while (!queue.empty()) { + auto* pack = queue.front(); + queue.pop(); + ss << " <- " << pack->id; + } + return contentpack_error(lastPack->id, lastPack->folder, ss.str()); +} + +/// @brief Resolve pack dependencies +/// @param pack current pack +/// @param packs all available packs repository +/// @param allNames all already done or enqueued packs +/// @param added packs with all dependencies resolved +/// @param queue current pass queue +/// @param resolveWeaks make weak dependencies resolved if found but not added to queue +/// @return true if all dependencies are already added or not found (optional/weak) +/// @throws contentpack_error if required dependency is not found +static bool resolve_dependencies ( + ContentPack* pack, + std::unordered_map& packs, + std::vector& allNames, + std::vector& added, + std::queue& queue, + bool resolveWeaks +) { + bool satisfied = true; + for (auto& dep : pack->dependencies) { + if (util::contains(added, dep.id)) { + continue; + } + auto found = packs.find(dep.id); + bool exists = found != packs.end(); + if (!exists && dep.level == DependencyLevel::required) { + throw contentpack_error(pack->id, pack->folder, "missing dependency '"+dep.id+"'"); + } + if (!exists) { + // ignored for optional or weak dependencies + continue; + } + if (resolveWeaks && dep.level == DependencyLevel::weak) { + // dependency pack is found but not added yet + // resolveWeaks is used on second iteration, so it's will not be added + continue; + } + + if (!util::contains(allNames, dep.id) && dep.level != DependencyLevel::weak) { + allNames.push_back(dep.id); + queue.push(&found->second); + } + satisfied = false; + } + return satisfied; +} + +std::vector PacksManager::assembly(const std::vector& names) { + std::vector allNames = names; + std::vector added; + std::queue queue; + std::queue queue2; + + for (auto& name : names) { + auto found = packs.find(name); + if (found == packs.end()) { + throw contentpack_error(name, fs::path(""), "pack not found"); + } + queue.push(&found->second); + } + + bool resolveWeaks = false; + while (!queue.empty()) { + int addedInIteration = 0; + while (!queue.empty()) { + auto* pack = queue.front(); + queue.pop(); + + if (resolve_dependencies(pack, packs, allNames, added, queue, resolveWeaks)) { + added.push_back(pack->id); + addedInIteration++; + } else { + queue2.push(pack); + } + } + std::swap(queue, queue2); + + // nothing done but deferring + if (addedInIteration == 0 && resolveWeaks) { + throw on_circular_dependency(queue); + } + resolveWeaks = true; + } + return added; +} diff --git a/src/content/PacksManager.h b/src/content/PacksManager.h new file mode 100644 index 00000000..96071e01 --- /dev/null +++ b/src/content/PacksManager.h @@ -0,0 +1,36 @@ +#ifndef CONTENT_PACKS_MANAGER_H_ +#define CONTENT_PACKS_MANAGER_H_ + +#include "ContentPack.h" + +#include +#include +#include + +namespace fs = std::filesystem; + +class PacksManager { + std::unordered_map packs; + std::vector sources; +public: + PacksManager(); + + /// @brief Set content packs sources (search folders) + void setSources(std::vector sources); + + /// @brief Scan sources and collect all found packs excluding duplication. + /// Scanning order depends on sources order + void scan(); + + /// @brief Get all found packs + std::vector getAllNames(); + + /// @brief Resolve all dependencies and fix packs order + /// @param names required packs (method can add extra packs) + /// @return resulting ordered vector of pack names + /// @throws contentpack_error if required dependency not found or + /// circular dependency detected + std::vector assembly(const std::vector& names); +}; + +#endif // CONTENT_PACKS_MANAGER_H_ diff --git a/src/util/listutil.h b/src/util/listutil.h index 1ed07d2f..911aacba 100644 --- a/src/util/listutil.h +++ b/src/util/listutil.h @@ -1,13 +1,20 @@ #ifndef UTIL_LISTUTIL_H_ #define UTIL_LISTUTIL_H_ +#include #include +#include namespace util { template - bool contains(std::vector vec, const T& value) { + bool contains(const std::vector& vec, const T& value) { return std::find(vec.begin(), vec.end(), value) != vec.end(); } + + template + bool contains(const std::queue& queue, const T& value) { + return std::find(queue.begin(), queue.end(), value) != queue.end(); + } } #endif // UTIL_LISTUTIL_H_