From 7aa4c33721ab9c0238b8caf49f7305e9d1052ac2 Mon Sep 17 00:00:00 2001 From: KotIsOff Date: Tue, 26 Aug 2025 01:52:26 +0300 Subject: [PATCH] dependency version operators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit а также сделал и вынес Version в ContentPackVersion ну и подправил луа скрипты. требуются тесты --- res/layouts/pages/content.xml.lua | 84 +++++++++++++++++++++++- src/content/ContentPack.cpp | 41 ++++++++++-- src/content/ContentPack.hpp | 10 ++- src/content/ContentPackVersion.cpp | 66 +++++++++++++++++++ src/content/ContentPackVersion.hpp | 51 ++++++++++++++ src/content/PacksManager.cpp | 14 +++- src/logic/scripting/lua/libs/libpack.cpp | 2 +- 7 files changed, 255 insertions(+), 13 deletions(-) create mode 100644 src/content/ContentPackVersion.cpp create mode 100644 src/content/ContentPackVersion.hpp diff --git a/res/layouts/pages/content.xml.lua b/res/layouts/pages/content.xml.lua index 8ce1cebc..1fe5c160 100644 --- a/res/layouts/pages/content.xml.lua +++ b/res/layouts/pages/content.xml.lua @@ -176,6 +176,81 @@ function place_pack(panel, packinfo, callback, position_func) end end +local Version = {}; + +function Version.matches_pattern(version) + for _, letter in string.gmatch(version, "%s+") do + if type(letter) ~= "number" or letter ~= "." then + return false; + end + + local t = string.split(version, "."); + + return #t == 2 or #t == 3; + end +end + +function Version.__equal(ver1, ver2) + return ver1[1] == ver2[1] and ver1[2] == ver2[2] and ver1[3] == ver2[3]; +end + +function Version.__more(ver1, ver2) + if ver1[1] ~= ver2[1] then return ver1[1] > ver2[1] end; + if ver1[2] ~= ver2[2] then return ver1[2] > ver2[2] end; + return ver1[3] > ver2[3]; +end + +function Version.__less(ver1, ver2) + return Version.__more(ver2, ver1); +end + +function Version.__more_or_equal(ver1, ver2) + return not Version.__less(ver1, ver2); +end + +function Version.__less_or_equal(ver1, ver2) + return not Version.__more(ver1, ver2); +end + +function Version.compare(op, ver1, ver2) + ver1 = string.split(ver1, "."); + ver2 = string.split(ver2, "."); + + if op == "=" then return Version.__equal(ver1, ver2); + elseif op == ">" then return Version.__more(ver1, ver2); + elseif op == "<" then return Version.__less(ver1, ver2); + elseif op == ">=" then return Version.__more_or_equal(ver1, ver2); + elseif op == "<=" then return Version.__less_or_equal(ver1, ver2); + else return false; end +end + +function Version.parse(version) + local op = string.sub(version, 1, 2); + if op == ">=" or op == "=>" then + return ">=", string.sub(version, #op + 1); + elseif op == "<=" or op == "=<" then + return "<=", string.sub(version, #op + 1); + end + + op = string.sub(version, 1, 1); + if op == ">" or op == "<" then + return op, string.sub(version, #op + 1); + end + + return "=", version; +end + +local function compare_version(dependent_version, actual_version) + if Version.matches_pattern(dependent_version) and Version.matches_pattern(actual_version) then + local op, dep_ver = Version.parse_version(dependent_version); + Version.compare(op, dep_ver, actual_version); + elseif dependent_version == "*" or dependent_version == actual_version then + return true; + else + return false; + end +end + function check_dependencies(packinfo) if packinfo.dependencies == nil then return @@ -190,9 +265,14 @@ function check_dependencies(packinfo) ) end + local dep_pack = pack.get_info(depid); - if depver ~= "*" and depver ~= dep_pack.version then - return string.format("%s (%s@%s != %s)", gui.str("error.dependency-version-not-met"), depid, dep_pack.version, depver); + + if not compare_version(depver, dep_pack.version) then + local op, ver = Version.parse(depver); + + print(string.format("%s: %s !%s %s (%s)", gui.str("error.dependency-version-not-met"), dep_pack.version, op, ver, depid)); + return string.format("%s: %s != %s (%s)", gui.str("error.dependency-version-not-met"), dep_pack.version, ver, depid); end if table.has(packs_installed, packinfo.id) then diff --git a/src/content/ContentPack.cpp b/src/content/ContentPack.cpp index 8c3ea541..49d7ee1f 100644 --- a/src/content/ContentPack.cpp +++ b/src/content/ContentPack.cpp @@ -133,14 +133,43 @@ ContentPack ContentPack::read(const io::path& folder) { break; } - std::string depVersion = "*"; - size_t version_pos = depName.rfind("@"); - if (version_pos != std::string::npos){ - depVersion = depName.substr(version_pos + 1); - depName = depName.substr(0, version_pos); + std::string depVer = "*"; + std::string depVerOperator = "="; + + size_t versionPos = depName.rfind("@"); + if (versionPos != std::string::npos) { + depVer = depName.substr(versionPos + 1); + depName = depName.substr(0, versionPos); + + if (depVer.size() >= 2) { + std::string op = depVer.substr(0, 2); + std::uint8_t op_size = 0; + + // Two symbol operators + if (op == ">=" || op == "=>" || op == "<=" || op == "=<") { + op_size = 2; + depVerOperator = op; + } + + // One symbol operators + else { + op = depVer.substr(0, 1); + + if (op == ">" || op == "<") { + op_size = 1; + depVerOperator = op; + } + } + + depVer = depVer.substr(op_size); + } else { + if (depVer == ">" || depVer == "<"){ + depVer = "*"; + } + } } - pack.dependencies.push_back({level, depName, depVersion}); + pack.dependencies.push_back({level, depName, depVer, depVerOperator}); } } diff --git a/src/content/ContentPack.hpp b/src/content/ContentPack.hpp index f8bc760c..0069553a 100644 --- a/src/content/ContentPack.hpp +++ b/src/content/ContentPack.hpp @@ -20,11 +20,16 @@ public: io::path folder, const std::string& message ); - + std::string getPackId() const; io::path getFolder() const; }; +enum class DependencyVersionOperator { + equal, more, less, + more_or_equal, less_or_equal +}; + enum class DependencyLevel { required, // dependency must be installed optional, // dependency will be installed if found @@ -35,7 +40,8 @@ enum class DependencyLevel { struct DependencyPack { DependencyLevel level; std::string id; - std::string verison; + std::string version; + std::string op; }; struct ContentPackStats { diff --git a/src/content/ContentPackVersion.cpp b/src/content/ContentPackVersion.cpp new file mode 100644 index 00000000..b8a0564b --- /dev/null +++ b/src/content/ContentPackVersion.cpp @@ -0,0 +1,66 @@ +#include "ContentPackVersion.hpp" + +#include +#include +#include + +#include "coders/commons.hpp" + +Version::Version(const std::string& version) { + major = 0; + minor = 0; + patch = 0; + + std::vector parts; + + std::stringstream ss(version); + std::string part; + while (std::getline(ss, part, '.')) { + if (!part.empty()) { + parts.push_back(std::stoi(part)); + } + } + + if (parts.size() > 0) major = parts[0]; + if (parts.size() > 1) minor = parts[1]; + if (parts.size() > 2) patch = parts[2]; +} + +DependencyVersionOperator Version::string_to_operator(const std::string& op) { + if (op == "=") + return DependencyVersionOperator::equal; + else if (op == ">") + return DependencyVersionOperator::more; + else if (op == "<") + return DependencyVersionOperator::less; + else if (op == ">=" || op == "=>") + return DependencyVersionOperator::more_or_equal; + else if (op == "<=" || op == "=<") + return DependencyVersionOperator::less_or_equal; + else return DependencyVersionOperator::equal; +} + +bool isNumber(const std::string& s) { + return !s.empty() && std::all_of(s.begin(), s.end(), ::is_digit); +} + +bool Version::matches_pattern(const std::string& version) { + for (char c : version) { + if (!isdigit(c) && c != '.') { + return false; + } + } + + std::stringstream ss(version); + + std::vector parts; + std::string part; + while (std::getline(ss, part, '.')) { + if (part.empty()) return false; + if (!isNumber(part)) return false; + + parts.push_back(part); + } + + return parts.size() == 2 || parts.size() == 3; +} \ No newline at end of file diff --git a/src/content/ContentPackVersion.hpp b/src/content/ContentPackVersion.hpp new file mode 100644 index 00000000..ef298b9e --- /dev/null +++ b/src/content/ContentPackVersion.hpp @@ -0,0 +1,51 @@ +#include + +#include "content/ContentPack.hpp" + +class Version { + public: + int major; + int minor; + int patch; + + Version(const std::string& version); + + bool operator==(const Version& other) const { + return major == other.major && minor == other.minor && patch == other.patch; + } + + bool operator<(const Version& other) const { + if (major != other.major) return major < other.major; + if (minor != other.minor) return minor < other.minor; + return patch < other.patch; + } + + + bool operator>(const Version& other) const { + return other < *this; + } + + bool operator>=(const Version& other) const { + return !(*this < other); + } + + bool operator<=(const Version& other) const { + return !(*this > other); + } + + bool process_operator(const std::string& op, const Version& other) const { + auto dep_op = Version::string_to_operator(op); + + switch(dep_op) { + case DependencyVersionOperator::equal: return *this == other; + case DependencyVersionOperator::more: return *this > other; + case DependencyVersionOperator::less: return *this < other; + case DependencyVersionOperator::less_or_equal: return *this <= other; + case DependencyVersionOperator::more_or_equal: return *this >= other; + default: return false; + } + } + + static DependencyVersionOperator string_to_operator(const std::string& op); + static bool matches_pattern(const std::string& version); +}; \ No newline at end of file diff --git a/src/content/PacksManager.cpp b/src/content/PacksManager.cpp index c898d590..1e2ed49f 100644 --- a/src/content/PacksManager.cpp +++ b/src/content/PacksManager.cpp @@ -3,6 +3,7 @@ #include #include +#include "ContentPackVersion.hpp" #include "util/listutil.hpp" PacksManager::PacksManager() = default; @@ -105,12 +106,21 @@ static bool resolve_dependencies( // added continue; } - if (dep.verison == "*" || dep.verison == found->second.version){ + + auto dep_pack = found -> second; + + if (Version::matches_pattern(dep.version) && Version::matches_pattern(dep_pack.version) + && Version(dep_pack.version) + .process_operator(dep.op, Version(dep.version)) + ) { // dependency pack version meets the required one continue; + } else if (dep.version == "*" || dep.version == dep_pack.version){ + // fallback: dependency pack version also meets required one + continue; } else { throw contentpack_error( - dep.id, io::path(), "does not meet required version '" + dep.verison +"' of '" + pack->id + "'" + dep.id, io::path(), "does not meet required version '" + dep.op + dep.version +"' of '" + pack->id + "'" ); } diff --git a/src/logic/scripting/lua/libs/libpack.cpp b/src/logic/scripting/lua/libs/libpack.cpp index be38fcb2..fc5e2252 100644 --- a/src/logic/scripting/lua/libs/libpack.cpp +++ b/src/logic/scripting/lua/libs/libpack.cpp @@ -115,7 +115,7 @@ static int l_pack_get_info( throw std::runtime_error(""); } - lua::pushfstring(L, "%s%s@%s", prefix.c_str(), dpack.id.c_str(), dpack.verison.c_str()); + lua::pushfstring(L, "%s%s@%s%s", prefix.c_str(), dpack.id.c_str(), dpack.op.c_str(), dpack.version.c_str()); lua::rawseti(L, i + 1); } lua::setfield(L, "dependencies");