Skip to content

Commit

Permalink
Initial "breaks" error description.
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexIIL committed Jul 24, 2023
1 parent 95232c6 commit 84a7c49
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public QuiltJsonButton(QuiltGuiSyncBase parent, String text, String icon, QuiltB
}

public static QuiltJsonButton createUserSupportButton(QuiltGuiSyncBase parent) {
QuiltLoaderText text = QuiltLoaderText.of("button.quilt_forum.user_support");
QuiltLoaderText text = QuiltLoaderText.translate("button.quilt_forum.user_support");
QuiltJsonButton button = new QuiltJsonButton(parent, text.toString(), null, QuiltBasicButtonAction.OPEN_WEB_URL);
button.arg("url", "https://forum.quiltmc.org/c/support/9");
return button;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@

package org.quiltmc.loader.impl.gui;

import java.util.Collections;
import java.util.IllegalFormatException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.quiltmc.loader.api.gui.QuiltLoaderText;
import org.quiltmc.loader.impl.plugin.gui.I18n;
Expand All @@ -26,14 +29,21 @@
@QuiltLoaderInternal(QuiltLoaderInternalType.NEW_INTERNAL)
public final class QuiltLoaderTextImpl implements QuiltLoaderText {

private static final Set<String> incorrectlyUntranslatedKeys = Collections.newSetFromMap(new ConcurrentHashMap<>());

private final String translationKey;
private final Object[] extra;
boolean translate;

public QuiltLoaderTextImpl(String key, boolean translate, Object... args) {
this.translationKey = key;
this.extra = args;
this.translate = true;
this.translate = translate;
if (!translate && !key.equals(I18n.translate(key))) {
if (incorrectlyUntranslatedKeys.add(key)) {
new Throwable("Incorrectly untranslated key '" + key + "'").printStackTrace();
}
}
}

@Override
Expand Down
178 changes: 176 additions & 2 deletions src/main/java/org/quiltmc/loader/impl/plugin/SolverErrorHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.quiltmc.loader.impl.plugin.quilt.DisabledModIdDefinition;
import org.quiltmc.loader.impl.plugin.quilt.MandatoryModIdDefinition;
import org.quiltmc.loader.impl.plugin.quilt.OptionalModIdDefintion;
import org.quiltmc.loader.impl.plugin.quilt.QuiltRuleBreakOnly;
import org.quiltmc.loader.impl.plugin.quilt.QuiltRuleDepOnly;
import org.quiltmc.loader.impl.util.QuiltLoaderInternal;
import org.quiltmc.loader.impl.util.QuiltLoaderInternalType;
Expand Down Expand Up @@ -180,6 +181,10 @@ private boolean reportKnownSolverError(List<RuleLink> rootRules) {
return true;
}

if (reportBreakingMods(rootRules)) {
return true;
}

return false;
}

Expand Down Expand Up @@ -371,7 +376,7 @@ private boolean reportDuplicateMandatoryMods(List<RuleLink> rootRules) {
// 1..N is either MandatoryModIdDefinition or DisabledModIdDefinition (and the mod is mandatory)

if (rootRules.size() < 3) {
return false;
return false;
}

// Step 1: Find the single OptionalModIdDefinition
Expand Down Expand Up @@ -483,6 +488,75 @@ private boolean reportDuplicateMandatoryMods(List<RuleLink> rootRules) {
return true;
}

private boolean reportBreakingMods(List<RuleLink> rootRules) {
if (rootRules.size() != 2) {
return false;
}

// ROOT[0] = A=RuleLink<MandatoryModIdDefinition>
// -> B=OptionLink<ModLoadOption> -> nothing
// ROOT[1] = C=RuleLink<MandatoryModIdDefinition>
// -> D=OptionLink<ModLoadOption>
// -> E=RuleLink<QuiltRuleBreakOnly> -> B

RuleLink ruleA = rootRules.get(0);
RuleLink ruleC = rootRules.get(1);
MandatoryModIdDefinition modA, modC;
OptionLink linkB, linkD;

if (ruleA.rule instanceof MandatoryModIdDefinition) {
modA = (MandatoryModIdDefinition) ruleA.rule;
linkB = ruleA.to.get(0);
} else {
return false;
}

if (ruleC.rule instanceof MandatoryModIdDefinition) {
modC = (MandatoryModIdDefinition) ruleC.rule;
linkD = ruleC.to.get(0);
} else {
return false;
}

if (linkB.to.size() > linkD.to.size()) {
RuleLink tempRule = ruleA;
MandatoryModIdDefinition tempMod = modA;
OptionLink tempLink = linkB;

ruleA = ruleC;
modA = modC;
linkB = linkD;

ruleC = tempRule;
modC = tempMod;
linkD = tempLink;
}

if (!linkB.to.isEmpty() || linkD.to.size() != 1) {
return false;
}

RuleLink linkE = linkD.to.get(0);

if (!(linkE.rule instanceof QuiltRuleBreakOnly)) {
return false;
}

QuiltRuleBreakOnly ruleE = (QuiltRuleBreakOnly) linkE.rule;
if (linkE.to.size() != 1 || !linkE.to.contains(linkB)) {
return false;
}

ModDependencyIdentifier modOn = ruleE.publicDep.id();
VersionRange versionsOn = ruleE.publicDep.versionRange();
ModLoadOption from = modC.option;
Set<ModLoadOption> allBreakingOptions = new LinkedHashSet<>();
allBreakingOptions.addAll(ruleE.getConflictingOptions());
this.errors.add(new BreakageError(modOn, versionsOn, from, allBreakingOptions));

return true;
}

/**
* A solver error which might have multiple real errors merged into one for display.
*/
Expand Down Expand Up @@ -514,7 +588,7 @@ void report(QuiltPluginManagerImpl manager) {
QuiltDisplayedError error = manager.theQuiltPluginContext.reportError(
QuiltLoaderText.translate("error.unhandled_solver")
);
error.appendDescription(QuiltLoaderText.of("error.unhandled_solver.desc"));
error.appendDescription(QuiltLoaderText.translate("error.unhandled_solver.desc"));
error.addOpenQuiltSupportButton();
error.appendReportText("Unhandled solver error involving the following rules:");

Expand Down Expand Up @@ -627,4 +701,104 @@ void report(QuiltPluginManagerImpl manager) {
}
}
}

static class BreakageError extends SolverError {
final ModDependencyIdentifier modOn;
final VersionRange versionsOn;
final Set<ModLoadOption> from = new LinkedHashSet<>();
final Set<ModLoadOption> allBreakingOptions;

BreakageError(ModDependencyIdentifier modOn, VersionRange versionsOn, ModLoadOption from, Set<
ModLoadOption> allBreakingOptions) {
this.modOn = modOn;
this.versionsOn = versionsOn;
this.from.add(from);
this.allBreakingOptions = allBreakingOptions;
}

@Override
boolean mergeInto(SolverError into) {
if (into instanceof BreakageError) {
BreakageError depDst = (BreakageError) into;
if (!modOn.equals(depDst.modOn) || !versionsOn.equals(depDst.versionsOn)) {
return false;
}
depDst.from.addAll(from);
return true;
}
return false;
}

@Override
void report(QuiltPluginManagerImpl manager) {

boolean transitive = false;

// Title:
// "BuildCraft" breaks with [version 1.5.1] of "Quilt Standard Libraries", but it's present!

// Description:
// BuildCraft is loaded from '<mods>/buildcraft-9.0.0.jar'
ModLoadOption mandatoryMod = from.iterator().next();
String rootModName = from.size() > 1 ? from.size() + " mods" : mandatoryMod.metadata().name();

QuiltLoaderText first = VersionRangeDescriber.describe(
rootModName, versionsOn, modOn.id(), false, transitive
);

Object[] secondData = new Object[allBreakingOptions.size() == 1 ? 1 : 0];
String secondKey = "error.break.";
if (allBreakingOptions.size() > 1) {
secondKey += "multi_conflict";
} else {
secondKey += "single_conflict";
secondData[0] = allBreakingOptions.iterator().next().version().toString();
}
QuiltLoaderText second = QuiltLoaderText.translate(secondKey + ".title", secondData);
QuiltLoaderText title = QuiltLoaderText.translate("error.break.join.title", first, second);
QuiltDisplayedError error = manager.theQuiltPluginContext.reportError(title);

setIconFromMod(manager, mandatoryMod, error);

Map<Path, ModLoadOption> realPaths = new LinkedHashMap<>();

for (ModLoadOption mod : from) {
Object[] modDescArgs = { mod.metadata().name(), manager.describePath(mod.from()) };
error.appendDescription(QuiltLoaderText.translate("info.root_mod_loaded_from", modDescArgs));
manager.getRealContainingFile(mod.from()).ifPresent(p -> realPaths.putIfAbsent(p, mod));
}

for (ModLoadOption mod : allBreakingOptions) {
Object[] modDescArgs = { mod.metadata().name(), manager.describePath(mod.from()) };
error.appendDescription(QuiltLoaderText.translate("info.root_mod_loaded_from", modDescArgs));
manager.getRealContainingFile(mod.from()).ifPresent(p -> realPaths.putIfAbsent(p, mod));
}

for (Map.Entry<Path, ModLoadOption> entry : realPaths.entrySet()) {
error.addFileViewButton(entry.getKey()).icon(entry.getValue().modCompleteIcon());
}

String issuesUrl = mandatoryMod.metadata().contactInfo().get("issues");
if (issuesUrl != null) {
error.addOpenLinkButton(
QuiltLoaderText.translate("button.mod_issue_tracker", mandatoryMod.metadata().name()), issuesUrl
);
}

StringBuilder report = new StringBuilder(rootModName);
report.append(" breaks");
if (VersionRange.ANY.equals(versionsOn)) {
report.append(" all versions of ");
} else {
report.append(" version ").append(versionsOn).append(" of ");
}
report.append(modOn);// TODO
report.append(", which is present!");
error.appendReportText(report.toString(), "");

for (ModLoadOption mod : from) {
error.appendReportText("- " + manager.describePath(mod.from()));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ public static QuiltLoaderText describe(String modName, VersionRange range, Strin
}

public static QuiltLoaderText describe(QuiltLoaderText modFrom, VersionRange range, String depName, boolean transitive) {
String titleKey = "error.dep." + (transitive ? "transitive." : "direct.");
return describe(modFrom, range, depName, true, transitive);
}

public static QuiltLoaderText describe(String modFrom, VersionRange range, String depName, boolean isDep, boolean transitive) {
return describe(QuiltLoaderText.of(modFrom), range, depName, isDep, transitive);
}

public static QuiltLoaderText describe(QuiltLoaderText modFrom, VersionRange range, String depName, boolean isDep, boolean transitive) {
String titleKey = "error." + (isDep ? "dep." : "break.") + (transitive ? "transitive." : "direct.");

if (range.size() != 1) {
return QuiltLoaderText.translate(getTransKey(titleKey, "ranged"), modFrom, range, depName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

@QuiltLoaderInternal(QuiltLoaderInternalType.NEW_INTERNAL)
public class QuiltRuleBreakOnly extends QuiltRuleBreak {
final ModDependency.Only publicDep;
public final ModDependency.Only publicDep;
final List<ModLoadOption> conflictingOptions;
final List<ModLoadOption> okayOptions;
final List<ModLoadOption> allOptions;
Expand Down Expand Up @@ -136,6 +136,18 @@ boolean hasAnyConflictingOptions() {
return !conflictingOptions.isEmpty();
}

public List<ModLoadOption> getConflictingOptions() {
return Collections.unmodifiableList(conflictingOptions);
}

public List<ModLoadOption> getOkayOptions() {
return Collections.unmodifiableList(okayOptions);
}

public List<ModLoadOption> getAllOptions() {
return Collections.unmodifiableList(allOptions);
}

@Override
public String toString() {
return publicDep.toString();
Expand Down Expand Up @@ -189,7 +201,7 @@ public void appendRuleDescription(Consumer<QuiltLoaderText> to) {
for (ModLoadOption option : conflictingOptions) {
to.accept(QuiltLoaderText.translate("solver.rule.mod_def.optional.source", option.describe()));
}
to.accept(QuiltLoaderText.translate("solver.rule.break.only.invalid", okayOptions.size()));
to.accept(QuiltLoaderText.translate("solver.rule.break.only.okay", okayOptions.size()));
for (ModLoadOption option : okayOptions) {
to.accept(QuiltLoaderText.translate("solver.rule.mod_def.optional.source", option.describe()));
}
Expand Down
15 changes: 15 additions & 0 deletions src/main/resources/lang/quilt_loader.properties
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ error.duplicate_mandatory=Duplicate mod: %s
error.duplicate_mandatory.mod=- %s
error.duplicate_mandatory.desc=Remove all but one.
error.dep.join.title=%s, %s
error.break.join.title=%s, %s

error.dep.direct.any.title=%s requires any version of %s
error.dep.direct.exact.title=%s requires version %s of %s
Expand All @@ -98,9 +99,23 @@ error.dep.transitive.range_inc_exc.title=%s transitively requires any version be
error.dep.transitive.range_exc_inc.title=%s transitively requires any version between %s (exclusive) and %s (inclusive) of %s
error.dep.transitive.range_exc_exc.title=%s transitively requires any version between %s (exclusive) and %s (exclusive) of %s

error.break.direct.any.title=%s breaks with all versions of %s
error.break.direct.exact.title=%s breaks with version %s of %s
error.break.direct.ranged.title=%s breaks with all versions in the range %s of %s
error.break.direct.greater.title=%s breaks with all version above %s of %s
error.break.direct.greater_equal.title=%s breaks with at least version %s or any newer version of %s
error.break.direct.lesser.title=%s breaks with all version below %s of %s
error.break.direct.lesser_equal.title=%s breaks version %s or any earlier version of %s
error.break.direct.range_inc_inc.title=%s breaks with all versions between %s (inclusive) and %s (inclusive) of %s
error.break.direct.range_inc_exc.title=%s breaks with all versions between %s (inclusive) and %s (exclusive) of %s
error.break.direct.range_exc_inc.title=%s breaks with all versions between %s (exclusive) and %s (inclusive) of %s
error.break.direct.range_exc_exc.title=%s breaks with all versions between %s (exclusive) and %s (exclusive) of %s

error.dep.missing.title=which is missing!
error.dep.single_mismatch.title=but only a different version is present: %s
error.dep.multi_mismatch.title=but only different versions are present!
error.break.single_conflict.title=but an incompatible version is present: %s
error.break.multi_conflict.title=but only incompatible versions are present!
error.unhandled_mod_file=Unknown file in the mods folder!
error.unhandled_mod_file.title=Unknown mod: %s
error.unhandled_mod_file.desc=Either:\n- Remove it.\n- Add a plugin which can load it\n- Add a ".disabled" file extension to prevent it from loading.
Expand Down

0 comments on commit 84a7c49

Please sign in to comment.