add log messages for found incompatibilities
This commit is contained in:
parent
7f22499f32
commit
6c564df5dd
|
@ -24,7 +24,7 @@ public class ModDependencyResolver {
|
|||
private final Map<String, DependencyEntry> dependencies = new HashMap<>();
|
||||
private final Map<String, DependencyEntry> breakings = new HashMap<>();
|
||||
private final Map<String, DependencyEntry> suggests = new HashMap<>();
|
||||
private final Map<String, SemVer> provides = new HashMap<>();
|
||||
private final Map<String, ProvidedMod> provides = new HashMap<>();
|
||||
private final Map<String, ModProperties> presentMods = new HashMap<>();
|
||||
|
||||
public ModDependencyResolver(Collection<ModProperties> mods) throws ResolverException {
|
||||
|
@ -45,7 +45,7 @@ public class ModDependencyResolver {
|
|||
}
|
||||
props.dependencies().getForType(ModDependencies.Type.PROVIDE).forEach(e -> {
|
||||
try {
|
||||
provides.put(e.id(), SemVerImpl.parse(e.range()));
|
||||
provides.put(e.id(), new ProvidedMod(e.id(), SemVerImpl.parse(e.range()), props));
|
||||
} catch (SemVerParseException ex) {
|
||||
LOGGER.warn("Version for {} ({}), provided by mod '{}' ({}) does not meet SemVer specifications. Mod will not be provided.",
|
||||
e.id(), e.range(), props.id(), props.name());
|
||||
|
@ -56,29 +56,39 @@ public class ModDependencyResolver {
|
|||
}
|
||||
|
||||
public Collection<ModProperties> solve() throws UnfulfilledDependencyException, BreakingModException {
|
||||
// Step 1: look for breakage declarations
|
||||
for (Map.Entry<String, DependencyEntry> e : breakings.entrySet()) {
|
||||
String key = e.getKey();
|
||||
DependencyEntry dependencyEntry = e.getValue();
|
||||
if (presentMods.containsKey(key) && dependencyEntry.range.versionMatches(presentMods.get(key).version())) {
|
||||
throw new BreakingModException(dependencyEntry.origin(), presentMods.get(key), dependencyEntry.range);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Combine present and provided mods, always use the latest version available
|
||||
Set<ModProperties> result = new HashSet<>();
|
||||
// Step 1: Combine present and provided mods, always use the latest version available
|
||||
Map<String, SemVer> presentOrProvided = new HashMap<>();
|
||||
presentMods.forEach((s, modProperties) -> presentOrProvided.put(s, modProperties.version()));
|
||||
provides.forEach((s, ver) -> {
|
||||
if (presentMods.containsKey(s)) {
|
||||
if (presentMods.get(s).version().compareTo(ver) < 0) {
|
||||
presentOrProvided.replace(s, ver);
|
||||
if (presentMods.get(s).version().compareTo(ver.version()) < 0) {
|
||||
presentOrProvided.replace(s, ver.version());
|
||||
}
|
||||
} else {
|
||||
presentOrProvided.put(s, ver);
|
||||
presentOrProvided.put(s, ver.version());
|
||||
}
|
||||
});
|
||||
|
||||
// Step 2: look for breakage declarations
|
||||
Collection<BreakingModException.Entry> breaks = new HashSet<>();
|
||||
for (Map.Entry<String, DependencyEntry> e : breakings.entrySet()) {
|
||||
String key = e.getKey();
|
||||
DependencyEntry dependencyEntry = e.getValue();
|
||||
if (presentOrProvided.containsKey(key) && dependencyEntry.range.versionMatches(presentOrProvided.get(key))) {
|
||||
breaks.add(new BreakingModException.Entry(dependencyEntry.origin(), Optional.ofNullable(presentMods.get(key)).orElseGet(() -> provides.get(key).source()), dependencyEntry.range));
|
||||
}
|
||||
}
|
||||
|
||||
if (!breaks.isEmpty()){
|
||||
breaks.forEach(e ->
|
||||
LOGGER.error("Mod {} ({}) breaks with mod {} ({}) for versions matching {}", e.source().id(), e.source().name(), e.broken().id(), e.broken().name(), e.range()));
|
||||
throw new BreakingModException(breaks);
|
||||
}
|
||||
|
||||
|
||||
Set<ModProperties> result = new HashSet<>();
|
||||
|
||||
|
||||
// Step 3: print out information about suggested mods
|
||||
for (Map.Entry<String, DependencyEntry> e : suggests.entrySet()) {
|
||||
String key = e.getKey();
|
||||
|
@ -96,14 +106,15 @@ public class ModDependencyResolver {
|
|||
}
|
||||
});
|
||||
|
||||
Collection<UnfulfilledDependencyException.Entry> unfulfilled = new HashSet<>();
|
||||
// Step 4.2: Check that all dependencies are satisfied by present or provided mods.
|
||||
for (Map.Entry<String, DependencyEntry> entry : dependencies.entrySet()) {
|
||||
String s = entry.getKey();
|
||||
DependencyEntry value = entry.getValue();
|
||||
if (!(presentMods.containsKey(s) && value.range.versionMatches(presentOrProvided.get(s)))) { // The dependency isn't fulfilled by any present mods
|
||||
if (!(provides.containsKey(s) && value.range.versionMatches(provides.get(s)))) { // The dependency also isn't fulfilled by any provided mods
|
||||
if (!(provides.containsKey(s) && value.range.versionMatches(provides.get(s).version()))) { // The dependency also isn't fulfilled by any provided mods
|
||||
if (value.origin.extensions().getOrDefault(BuiltinExtensions.LOADING_TYPE, "required").equals("required")) {
|
||||
throw new UnfulfilledDependencyException(value.origin, s, value.range);
|
||||
unfulfilled.add(new UnfulfilledDependencyException.Entry(value.origin, s, value.range));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
@ -113,9 +124,22 @@ public class ModDependencyResolver {
|
|||
result.add(value.origin());
|
||||
}
|
||||
|
||||
if (!unfulfilled.isEmpty()){
|
||||
unfulfilled.forEach(e -> {
|
||||
if (!presentOrProvided.containsKey(e.dependency)) {
|
||||
LOGGER.error("Mod {} ({}) depends on mod {} with a version matching {}", e.source().id(), e.source().name(), e.dependency(), e.range());
|
||||
} else {
|
||||
LOGGER.error("Mod {} ({}) depends on mod {} with a version matching {}, but a different version is present or provided: {}", e.source().id(), e.source().name(), e.dependency(), e.range(), presentOrProvided.get(e.dependency));
|
||||
}
|
||||
});
|
||||
throw new UnfulfilledDependencyException(unfulfilled);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private record ProvidedMod(String modId, SemVer version, ModProperties source){}
|
||||
|
||||
@AllArgsConstructor
|
||||
private enum DependencyType {
|
||||
EQ("=", Object::equals),
|
||||
|
@ -140,16 +164,19 @@ public class ModDependencyResolver {
|
|||
@AllArgsConstructor
|
||||
@Getter
|
||||
public static class BreakingModException extends Exception {
|
||||
private final ModProperties source, broken;
|
||||
private final VersionRange range;
|
||||
private final Collection<Entry> breaks;
|
||||
|
||||
public record Entry(ModProperties source, ModProperties broken, VersionRange range) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public static class UnfulfilledDependencyException extends Exception {
|
||||
private final ModProperties source;
|
||||
private final String dependency;
|
||||
private final VersionRange range;
|
||||
private final Collection<Entry> dependencies;
|
||||
|
||||
public record Entry(ModProperties source, String dependency, VersionRange range){}
|
||||
}
|
||||
|
||||
public static class ResolverException extends Exception {
|
||||
|
|
|
@ -22,7 +22,7 @@ public class ModUtil {
|
|||
} else {
|
||||
builder.append("\\- ");
|
||||
}
|
||||
builder.append(p.name()).append(" (").append(p.id()).append(") ").append(" ").append(p.version());
|
||||
builder.append(p.id()).append(" (").append(p.name()).append(") ").append(" ").append(p.version());
|
||||
i++;
|
||||
}
|
||||
return builder.toString();
|
||||
|
|
Loading…
Reference in a new issue