#include "PacksManager.hpp" #include "../util/listutil.hpp" #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); } } } void PacksManager::exclude(const std::string& id) { packs.erase(id); } std::vector PacksManager::getAllNames() const { std::vector names; for (auto& entry : packs) { names.push_back(entry.first); } return names; } std::vector PacksManager::getAll(const std::vector& names) const { std::vector packsList; for (auto& name : names) { auto found = packs.find(name); if (found == packs.end()) { throw contentpack_error(name, fs::path(""), "pack not found"); } packsList.push_back(found->second); } return packsList; } static contentpack_error on_circular_dependency(std::queue& queue) { const 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 ( const ContentPack* pack, const std::unordered_map& packs, std::vector& allNames, const 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(dep.id, fs::path(), "dependency of '"+pack->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) const { 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)) { if (util::contains(added, pack->id)) { throw contentpack_error(pack->id, pack->folder, "pack duplication"); } 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; } std::vector PacksManager::getNames(const std::vector& packs) { std::vector result; for (const auto& pack : packs) { result.push_back(pack.id); } return result; }