diff --git a/src/mods/LauncherMods.lua b/src/mods/LauncherMods.lua index 87e51acb..10922dea 100644 --- a/src/mods/LauncherMods.lua +++ b/src/mods/LauncherMods.lua @@ -215,12 +215,19 @@ function LauncherMods.checkDependencies(manifest, options, version, installedMan return m and not m.experimental end + local function conflictApplies(spec, other) + return not spec.range or (other and other.version + and Semver.satisfies(other.version, spec.range)) + end + -- (a) Conflicts declared by target manifest if type(manifest.conflictSpecs) == "table" then for _, spec in ipairs(manifest.conflictSpecs) do local conflictId = spec.id local installedOther = installedMap[conflictId] - if installedOther and isEnabled(conflictId) and not conflictIdsSeen[conflictId] then + if installedOther and isEnabled(conflictId) + and conflictApplies(spec, installedOther) + and not conflictIdsSeen[conflictId] then conflictIdsSeen[conflictId] = true hasIssues = true depsResult[#depsResult + 1] = { @@ -238,11 +245,12 @@ function LauncherMods.checkDependencies(manifest, options, version, installedMan -- (b) Reverse conflicts declared by installed mods against target manifest if manifest.id then + local installedTarget = installedMap[manifest.id] or manifest for _, other in ipairs(manifests) do if other.id ~= manifest.id and isEnabled(other.id) and not conflictIdsSeen[other.id] then local conflicts = other.conflictSpecs or {} for _, spec in ipairs(conflicts) do - if spec.id == manifest.id then + if spec.id == manifest.id and conflictApplies(spec, installedTarget) then conflictIdsSeen[other.id] = true hasIssues = true depsResult[#depsResult + 1] = { diff --git a/tests/mod_manifest_tests.lua b/tests/mod_manifest_tests.lua index f7d1061d..7d7edae3 100644 --- a/tests/mod_manifest_tests.lua +++ b/tests/mod_manifest_tests.lua @@ -550,6 +550,51 @@ local installedColorlib = Manifest.validate({ version = "1.0.0", entry = "main.lua", }, "mods/colorlib") +local unconditionalConflict = LauncherMods.checkDependencies(testTargetManifest, + nil, nil, { testTargetManifest, installedColorlib }) +check(unconditionalConflict.hasIssues == true, + "dependency resolver reports an unversioned conflict") + +local function rangeTarget(version, conflicts) + return Manifest.validate({ + id = "range_target", + name = "Range Target", + version = version, + entry = "main.lua", + conflicts = conflicts or {}, + }, "mods/range_target") +end +local function rangeSource(conflicts) + return Manifest.validate({ + id = "range_source", + name = "Range Source", + version = "1.0.0", + entry = "main.lua", + conflicts = conflicts or {}, + }, "mods/range_source") +end + +local forwardSource = rangeSource({ "range_target@<2.0.0" }) +local matchingTarget = rangeTarget("1.4.0") +local nonmatchingTarget = rangeTarget("2.0.0") +local forwardMatching = LauncherMods.checkDependencies(forwardSource, + nil, nil, { forwardSource, matchingTarget }) +check(forwardMatching.hasIssues == true and #forwardMatching.deps == 1, + "dependency resolver applies a matching forward conflict range") +local forwardNonmatching = LauncherMods.checkDependencies(forwardSource, + nil, nil, { forwardSource, nonmatchingTarget }) +check(forwardNonmatching.hasIssues == false and #forwardNonmatching.deps == 0, + "dependency resolver ignores a nonmatching forward conflict range") + +local reverseSource = rangeSource({ "range_target@<2.0.0" }) +local reverseMatching = LauncherMods.checkDependencies(matchingTarget, + nil, nil, { reverseSource, matchingTarget }) +check(reverseMatching.hasIssues == true and #reverseMatching.deps == 1, + "dependency resolver applies a matching reverse conflict range") +local reverseNonmatching = LauncherMods.checkDependencies(nonmatchingTarget, + nil, nil, { reverseSource, nonmatchingTarget }) +check(reverseNonmatching.hasIssues == false and #reverseNonmatching.deps == 0, + "dependency resolver ignores a nonmatching reverse conflict range") -- ------- scoped dependency tests local Json = require("src.link.Json") local scopedDepManifest = Manifest.validate({