Skip to content

Commit

Permalink
Adapt to the breaks clause
Browse files Browse the repository at this point in the history
  • Loading branch information
Matyrobbrt committed Dec 14, 2023
1 parent bd6fe05 commit 5ad65bf
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 66 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
spi_version=8.0.6-breaks-clause
spi_version=8.0.7-breaks-clause
mergetool_version=2.0.0
accesstransformers_version=10.0.1
coremods_version=6.0.0
Expand Down
143 changes: 79 additions & 64 deletions loader/src/main/java/net/neoforged/fml/loading/ModSorter.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,17 @@ public static LoadingModList sort(List<ModFile> mods, final List<EarlyLoadingExc
// We cannot build any list with duped mods. We have to abort immediately and report it
return LoadingModList.of(ms.systemMods, ms.systemMods.stream().map(mf->(ModInfo)mf.getModInfos().get(0)).collect(toList()), e);
}
final List<EarlyLoadingException.ExceptionData> warnings = new ArrayList<>();

// try and validate dependencies
final List<EarlyLoadingException.ExceptionData> failedList = Stream.concat(ms.verifyDependencyVersions(warnings).stream(), errors.stream()).toList();
// if we miss one or the other, we abort now
final DependencyResolutionResult resolutionResult = ms.verifyDependencyVersions();

final List<EarlyLoadingException.ExceptionData> warnings = new ArrayList<>();

final LoadingModList list;
if (!failedList.isEmpty()) {
list = LoadingModList.of(ms.systemMods, ms.systemMods.stream().map(mf->(ModInfo)mf.getModInfos().get(0)).collect(toList()), new EarlyLoadingException("failure to validate mod list", null, failedList));

// if we miss a dependency or detect an incompatibility, we abort now
if (!resolutionResult.versionResolution.isEmpty() || !resolutionResult.incompatibilities.isEmpty()) {
list = LoadingModList.of(ms.systemMods, ms.systemMods.stream().map(mf->(ModInfo)mf.getModInfos().get(0)).collect(toList()), new EarlyLoadingException("failure to validate mod list", null, resolutionResult.buildErrorMessages()));
} else {
// Otherwise, lets try and sort the modlist and proceed
EarlyLoadingException earlyLoadingException = null;
Expand All @@ -70,11 +74,12 @@ public static LoadingModList sort(List<ModFile> mods, final List<EarlyLoadingExc
list = LoadingModList.of(ms.modFiles, ms.sortedList, earlyLoadingException);
}

if (!warnings.isEmpty()) {
// If we have conflicts those are considered warnings
if (!resolutionResult.conflicts.isEmpty()) {
list.getWarnings().add(new EarlyLoadingException(
"found mod conflicts",
null,
warnings
resolutionResult.buildWarningMessages()
));
}
return list;
Expand Down Expand Up @@ -191,7 +196,36 @@ private void detectSystemMods(final Map<String, List<ModFile>> modFilesByFirstId
}
}

private List<EarlyLoadingException.ExceptionData> verifyDependencyVersions(final List<EarlyLoadingException.ExceptionData> warnings)
public record DependencyResolutionResult(
Collection<IModInfo.ModVersion> incompatibilities,
Collection<IModInfo.ModVersion> conflicts,
Collection<IModInfo.ModVersion> versionResolution,
Map<String, ArtifactVersion> modVersions
) {
public List<EarlyLoadingException.ExceptionData> buildWarningMessages() {
return conflicts.stream()
.map(mv -> new EarlyLoadingException.ExceptionData("fml.modloading.conflictingmod",
mv.getOwner(), mv.getModId(), mv.getOwner().getModId(), mv.getVersionRange(),
modVersions.get(mv.getModId()), mv.getReason().orElse("fml.modloading.conflictingmod.noreason")))
.toList();
}

public List<EarlyLoadingException.ExceptionData> buildErrorMessages() {
return Stream.concat(
versionResolution.stream()
.map(mv -> new EarlyLoadingException.ExceptionData(mv.getType() == IModInfo.DependencyType.REQUIRED ? "fml.modloading.missingdependency" : "fml.modloading.missingdependency.optional",
mv.getOwner(), mv.getModId(), mv.getOwner().getModId(), mv.getVersionRange(),
modVersions.getOrDefault(mv.getModId(), new DefaultArtifactVersion("null")))),
incompatibilities.stream()
.map(mv -> new EarlyLoadingException.ExceptionData("fml.modloading.incompatiblemod",
mv.getOwner(), mv.getModId(), mv.getOwner().getModId(), mv.getVersionRange(),
modVersions.get(mv.getModId()), mv.getReason().orElse("fml.modloading.incompatiblemod.noreason")))
)
.toList();
}
}

private DependencyResolutionResult verifyDependencyVersions()
{
final var modVersions = modFiles.stream()
.map(ModFile::getModInfos)
Expand All @@ -218,73 +252,54 @@ private List<EarlyLoadingException.ExceptionData> verifyDependencyVersions(final

final var incompatibleVersions = modRequirements.stream().filter(ver -> ver.getType() == IModInfo.DependencyType.INCOMPATIBLE)
.filter(ver -> modVersions.containsKey(ver.getModId()) && !this.modVersionNotContained(ver, modVersions))
.toList();
.collect(toSet());

final var conflictingVersions = modRequirements.stream().filter(ver -> ver.getType() == IModInfo.DependencyType.CONFLICTING)
.filter(ver -> modVersions.containsKey(ver.getModId()) && !this.modVersionNotContained(ver, modVersions))
.toList();
.collect(toSet());

if (!conflictingVersions.isEmpty()) {
LOGGER.error(
LogMarkers.LOADING,
"Conflicts between mods:\n{}\nIssues may arise. Continue at your own risk.",
"Conflicts between mods:\n{}\n\tIssues may arise. Continue at your own risk.",
conflictingVersions.stream()
.map(ver -> formatIncompatibleDependencyError(ver, "Conflicting", modVersions))
.map(ver -> formatIncompatibleDependencyError(ver, "conflicting", modVersions))
.collect(Collectors.joining("\n"))
);

conflictingVersions.stream()
.map(mv -> new EarlyLoadingException.ExceptionData("fml.modloading.conflictingmod",
mv.getOwner(), mv.getModId(), mv.getOwner().getModId(), mv.getVersionRange(),
modVersions.get(mv.getModId())))
.forEach(warnings::add);
}

if (!missingVersions.isEmpty() || !incompatibleVersions.isEmpty()) {
if (mandatoryMissing > 0) {
LOGGER.error(
LogMarkers.LOADING,
"Missing or unsupported mandatory dependencies:\n{}",
missingVersions.stream()
.filter(mv -> mv.getType() == IModInfo.DependencyType.REQUIRED)
.map(ver -> formatDependencyError(ver, modVersions))
.collect(Collectors.joining("\n"))
);
}
if (missingVersions.size() - mandatoryMissing > 0) {
LOGGER.error(
LogMarkers.LOADING,
"Unsupported installed optional dependencies:\n{}",
missingVersions.stream()
.filter(ver -> ver.getType() == IModInfo.DependencyType.OPTIONAL)
.map(ver -> formatDependencyError(ver, modVersions))
.collect(Collectors.joining("\n"))
);
}

if (!incompatibleVersions.isEmpty()) {
LOGGER.error(
LogMarkers.LOADING,
"Incompatibilities between mods:\n{}",
incompatibleVersions.stream()
.map(ver -> formatIncompatibleDependencyError(ver, "Incompatible", modVersions))
.collect(Collectors.joining("\n"))
);
}
if (mandatoryMissing > 0) {
LOGGER.error(
LogMarkers.LOADING,
"Missing or unsupported mandatory dependencies:\n{}",
missingVersions.stream()
.filter(mv -> mv.getType() == IModInfo.DependencyType.REQUIRED)
.map(ver -> formatDependencyError(ver, modVersions))
.collect(Collectors.joining("\n"))
);
}
if (missingVersions.size() - mandatoryMissing > 0) {
LOGGER.error(
LogMarkers.LOADING,
"Unsupported installed optional dependencies:\n{}",
missingVersions.stream()
.filter(ver -> ver.getType() == IModInfo.DependencyType.OPTIONAL)
.map(ver -> formatDependencyError(ver, modVersions))
.collect(Collectors.joining("\n"))
);
}

return Stream.concat(
missingVersions.stream()
.map(mv -> new EarlyLoadingException.ExceptionData(mv.getType() == IModInfo.DependencyType.REQUIRED ? "fml.modloading.missingdependency" : "fml.modloading.missingdependency.optional",
mv.getOwner(), mv.getModId(), mv.getOwner().getModId(), mv.getVersionRange(),
modVersions.getOrDefault(mv.getModId(), new DefaultArtifactVersion("null")))),
incompatibleVersions.stream()
.map(mv -> new EarlyLoadingException.ExceptionData("fml.modloading.incompatiblemod",
mv.getOwner(), mv.getModId(), mv.getOwner().getModId(), mv.getVersionRange(),
modVersions.get(mv.getModId())))
)
.toList();
if (!incompatibleVersions.isEmpty()) {
LOGGER.error(
LogMarkers.LOADING,
"Incompatibilities between mods:\n{}",
incompatibleVersions.stream()
.map(ver -> formatIncompatibleDependencyError(ver, "incompatible", modVersions))
.collect(Collectors.joining("\n"))
);
}
return Collections.emptyList();

return new DependencyResolutionResult(incompatibleVersions, conflictingVersions, missingVersions, modVersions);
}

private static String formatDependencyError(IModInfo.ModVersion dependency, Map<String, ArtifactVersion> modVersions)
Expand All @@ -302,10 +317,10 @@ private static String formatDependencyError(IModInfo.ModVersion dependency, Map<
private static String formatIncompatibleDependencyError(IModInfo.ModVersion dependency, String type, Map<String, ArtifactVersion> modVersions)
{
return String.format(
"\tMod ID: '%s', %s with: '%s', versions: '%s', Actual version: '%s'",
dependency.getModId(),
type,
"\tMod '%s' is %s with: '%s', versions: '%s'; Version found: '%s'",
dependency.getOwner().getModId(),
type,
dependency.getModId(),
dependency.getVersionRange(),
modVersions.get(dependency.getModId()).toString()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package net.neoforged.fml.loading.moddiscovery;

import com.mojang.logging.LogUtils;
import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.fml.loading.StringSubstitutor;
import net.neoforged.fml.loading.StringUtils;
import net.neoforged.neoforgespi.language.IConfigurable;
Expand Down Expand Up @@ -206,6 +207,7 @@ class ModVersion implements net.neoforged.neoforgespi.language.IModInfo.ModVersi
private final String modId;
private final VersionRange versionRange;
private final DependencyType type;
private final Optional<String> reason;
private final Ordering ordering;
private final DependencySide side;
private Optional<URL> referralUrl;
Expand All @@ -215,7 +217,20 @@ public ModVersion(final IModInfo owner, final IConfigurable config) {
this.modId = config.<String>getConfigElement("modId")
.orElseThrow(()->new InvalidModFileException("Missing required field modid in dependency", getOwningFile()));
this.type = config.<String>getConfigElement("type")
.map(str -> str.toUpperCase(Locale.ROOT)).map(DependencyType::valueOf).orElse(DependencyType.REQUIRED);
.map(str -> str.toUpperCase(Locale.ROOT)).map(DependencyType::valueOf).orElseGet(() -> {
final var mandatory = config.<Boolean>getConfigElement("mandatory");
if (mandatory.isPresent()) {
if (!FMLLoader.isProduction()) {
LOGGER.error("Mod '{}' uses deprecated 'mandatory' field in the dependency declaration for '{}'. Use the 'type' field and 'required'/'optional' instead", owner.getModId(), modId);
throw new InvalidModFileException("Deprecated 'mandatory' field is used in dependency", getOwningFile());
}

return mandatory.get() ? DependencyType.REQUIRED : DependencyType.OPTIONAL;
}

return DependencyType.REQUIRED;
});
this.reason = config.<String>getConfigElement("reason");
this.versionRange = config.<String>getConfigElement("versionRange")
.map(MavenVersionAdapter::createFromVersionSpec)
.orElse(UNBOUNDED);
Expand Down Expand Up @@ -247,6 +262,11 @@ public DependencyType getType() {
return type;
}

@Override
public Optional<String> getReason() {
return reason;
}

@Override
public Ordering getOrdering()
{
Expand Down

0 comments on commit 5ad65bf

Please sign in to comment.