mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-22 16:17:44 +00:00
Compat report, validation and commands
- Force-load every registrant and placement during pack validation and persist the findings with the Minecraft version in the validation cache - Turn an excluded dimension into a blocking validation error and suppress content-key warnings the gate already reported - Print the capped per-key listing after the validation lines on Bukkit and modded, ordered excluded, dropped, substituted - Log one summary line per world engine from the published validation result - Add /iris pack compat on Bukkit and modded with help and locale keys for every shipped language
This commit is contained in:
33 files changed
+1018
-28
No files matched your search
+16
-2
@@ -60,6 +60,7 @@ import java.util.function.Supplier;
|
||||
*/
|
||||
public final class IrisWorldGeneratorResolver {
|
||||
private static final int VALIDATION_STABILITY_ATTEMPTS = 2;
|
||||
private static final int COMPAT_BOOT_KEY_CAP = 3;
|
||||
private static final Object SNAPSHOT_VALIDATION_LOCK = new Object();
|
||||
private static final String IRIS_DIMENSION_NAMESPACE = "iris";
|
||||
private static final String PLOT_SQUARED_DISCOVERY_WORLD = "CheckingPlotSquaredGenerator";
|
||||
@@ -123,6 +124,9 @@ public final class IrisWorldGeneratorResolver {
|
||||
if (packDirectory != null && packFingerprint != null && !packFingerprint.isBlank()) {
|
||||
PackValidationRegistry.publish(packDirectory.toPath(), result, packFingerprint);
|
||||
}
|
||||
String minecraftVersion = IrisPlatforms.isBound() ? IrisPlatforms.get().minecraftVersion() : null;
|
||||
String compatSummary = PackValidator.compatSummary(result, minecraftVersion);
|
||||
String compatSuffix = compatSummary.isEmpty() ? "" : " " + compatSummary;
|
||||
if (!result.isLoadable()) {
|
||||
Iris.error("Pack '" + result.getPackName()
|
||||
+ "' FAILED validation - world and Studio creation with this pack will be refused. Reasons:");
|
||||
@@ -131,12 +135,22 @@ public final class IrisWorldGeneratorResolver {
|
||||
}
|
||||
} else if (!result.getWarnings().isEmpty()) {
|
||||
Iris.info("Pack '" + result.getPackName() + "' validated ("
|
||||
+ result.getWarnings().size() + " warning(s)).");
|
||||
+ result.getWarnings().size() + " warning(s))." + compatSuffix);
|
||||
for (String warning : result.getWarnings()) {
|
||||
Iris.warn(" [" + result.getPackName() + "] " + warning);
|
||||
}
|
||||
} else if (cached.isEmpty()) {
|
||||
Iris.success("Pack '" + result.getPackName() + "' validated.");
|
||||
if (compatSummary.isEmpty()) {
|
||||
Iris.success("Pack '" + result.getPackName() + "' validated.");
|
||||
} else {
|
||||
Iris.info("Pack '" + result.getPackName() + "' validated." + compatSuffix);
|
||||
}
|
||||
} else if (!compatSummary.isEmpty()) {
|
||||
Iris.info("Pack '" + result.getPackName() + "' validated." + compatSuffix);
|
||||
}
|
||||
// Cache hits carry the persisted findings, so the listing prints on every boot, not only after a miss.
|
||||
for (String line : PackValidator.compatBootLines(result, minecraftVersion, COMPAT_BOOT_KEY_CAP)) {
|
||||
Iris.warn(line);
|
||||
}
|
||||
}
|
||||
IrisStartupValidation.markPacksReady();
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package art.arcane.iris.core.commands;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.compat.PackCompatReport;
|
||||
import art.arcane.iris.core.pack.PackDirectoryResolver;
|
||||
import art.arcane.iris.core.pack.PackResourceCleanup;
|
||||
import art.arcane.iris.core.pack.PackValidationCache;
|
||||
@@ -34,10 +35,13 @@ import art.arcane.volmlib.util.director.annotations.Director;
|
||||
import art.arcane.volmlib.util.director.annotations.Param;
|
||||
import art.arcane.volmlib.util.localization.TextKey;
|
||||
|
||||
import art.arcane.iris.spi.IrisPlatforms;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import art.arcane.iris.core.localization.IrisLanguage;
|
||||
import art.arcane.iris.core.localization.BukkitCommandMessages;
|
||||
@@ -250,6 +254,76 @@ public class CommandPack implements DirectorExecutor {
|
||||
reportResult(s, result);
|
||||
}
|
||||
|
||||
@Director(description = "Show pack content that cannot generate on this Minecraft version", descriptionKey = "iris.director.commandpack.director.show_content_cannot_generate_this_minecraft_version", aliases = {"cp"})
|
||||
public void compat(
|
||||
@Param(description = "The pack folder name to report on, or * for every pack", descriptionKey = "iris.director.commandpack.param.pack_folder_name_compat", defaultValue = "*")
|
||||
String pack
|
||||
) {
|
||||
VolmitSender s = sender();
|
||||
if (wantsAllPacks(pack)) {
|
||||
Map<String, PackValidationResult> snapshot = PackValidationRegistry.snapshot();
|
||||
if (snapshot.isEmpty()) {
|
||||
s.sendMessage(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_NO_VALIDATION_RESULTS_RECORDED_RUN_IRIS_PACK_VALIDATE_FIRST));
|
||||
return;
|
||||
}
|
||||
for (Map.Entry<String, PackValidationResult> entry : snapshot.entrySet()) {
|
||||
for (String line : compatOutput(entry.getKey(), entry.getValue())) {
|
||||
s.sendMessage(line);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (String line : compatOutput(pack, PackValidationRegistry.get(pack))) {
|
||||
s.sendMessage(line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The compat listing for one pack, straight from the persisted validation so no pack is reloaded. Every key is
|
||||
* printed with every subject; the boot listing is the capped form of the same data.
|
||||
*/
|
||||
static List<String> compatOutput(String pack, PackValidationResult result) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
if (result == null) {
|
||||
lines.add(IrisLanguage.text(
|
||||
BukkitRuntimeMessages.COMMAND_PACK_NO_VALIDATION_RESULT_RUN_IRIS_PACK_VALIDATE,
|
||||
MessageArgument.untrusted("pack", String.valueOf(pack)),
|
||||
MessageArgument.untrusted("pack2", String.valueOf(pack))));
|
||||
return lines;
|
||||
}
|
||||
String version = compatVersion(result);
|
||||
if (result.getCompatFindings().isEmpty()) {
|
||||
lines.add(IrisLanguage.text(
|
||||
BukkitRuntimeMessages.COMMAND_PACK_COMPAT_NONE,
|
||||
MessageArgument.untrusted("pack", pack),
|
||||
MessageArgument.untrusted("version", version)));
|
||||
return lines;
|
||||
}
|
||||
lines.add(IrisLanguage.text(
|
||||
BukkitRuntimeMessages.COMMAND_PACK_COMPAT_HEADER,
|
||||
MessageArgument.untrusted("pack", pack),
|
||||
MessageArgument.untrusted("version", version)));
|
||||
for (String line : PackCompatReport.of(result.getCompatFindings()).keyLines(0)) {
|
||||
lines.add(IrisLanguage.text(
|
||||
BukkitRuntimeMessages.COMMAND_PACK_MESSAGE_2,
|
||||
MessageArgument.untrusted("value", line)));
|
||||
}
|
||||
lines.add(IrisLanguage.text(BukkitRuntimeMessages.COMMAND_PACK_COMPAT_REMEDY));
|
||||
return lines;
|
||||
}
|
||||
|
||||
/** The live Minecraft version, falling back to the one the validation ran against. */
|
||||
static String compatVersion(PackValidationResult result) {
|
||||
if (IrisPlatforms.isBound()) {
|
||||
String live = IrisPlatforms.get().minecraftVersion();
|
||||
if (live != null && !live.isBlank()) {
|
||||
return live;
|
||||
}
|
||||
}
|
||||
String persisted = result.getMinecraftVersion();
|
||||
return persisted == null || persisted.isBlank() ? "unknown" : persisted;
|
||||
}
|
||||
|
||||
private PackValidationResult runValidate(VolmitSender s, File packFolder) {
|
||||
try {
|
||||
PackValidationResult result = PackValidator.validate(packFolder);
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package art.arcane.iris.core.commands;
|
||||
|
||||
import art.arcane.iris.core.compat.CompatAction;
|
||||
import art.arcane.iris.core.compat.CompatFinding;
|
||||
import art.arcane.iris.core.compat.CompatRegistry;
|
||||
import art.arcane.iris.core.pack.PackValidationResult;
|
||||
import art.arcane.volmlib.util.director.compat.DirectorAnnotationCompatibility;
|
||||
import art.arcane.volmlib.util.director.runtime.DirectorNodeDescriptor;
|
||||
import art.arcane.volmlib.util.director.runtime.DirectorParameterDescriptor;
|
||||
import art.arcane.iris.spi.IrisPlatform;
|
||||
import art.arcane.iris.spi.IrisPlatforms;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CommandPackCompatTest {
|
||||
private IrisPlatform previousPlatform;
|
||||
|
||||
/** The persisted Minecraft version is only used when no platform is bound, so pin that here. */
|
||||
@Before
|
||||
public void unbindPlatform() {
|
||||
previousPlatform = IrisPlatforms.isBound() ? IrisPlatforms.get() : null;
|
||||
IrisPlatforms.unbind();
|
||||
}
|
||||
|
||||
@After
|
||||
public void rebindPlatform() {
|
||||
IrisPlatforms.unbind();
|
||||
if (previousPlatform != null) {
|
||||
IrisPlatforms.bind(previousPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
private static CompatFinding block(CompatAction action, String subjectKey) {
|
||||
return new CompatFinding(
|
||||
CompatRegistry.BLOCK, "minecraft:sulfur", action, "Biome", subjectKey, "layers[0].palette[1]");
|
||||
}
|
||||
|
||||
private static PackValidationResult result(List<CompatFinding> findings) {
|
||||
return new PackValidationResult("overworld", List.of(), List.of(), 1L, findings, "26.1.2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unknownPackIsReportedWithoutAnyCompatLines() {
|
||||
List<String> lines = CommandPack.compatOutput("nope", null);
|
||||
|
||||
assertEquals(1, lines.size());
|
||||
assertTrue(lines.getFirst(), lines.getFirst().contains("No validation result for 'nope'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void packWithoutFindingsSaysSoAndNamesTheVersion() {
|
||||
List<String> lines = CommandPack.compatOutput("overworld", result(List.of()));
|
||||
|
||||
assertEquals(1, lines.size());
|
||||
assertTrue(lines.getFirst(), lines.getFirst().contains("Pack 'overworld' uses no content"));
|
||||
assertTrue(lines.getFirst(), lines.getFirst().contains("26.1.2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findingsArePrintedUncappedWithTheFallbackRemedy() {
|
||||
List<String> lines = CommandPack.compatOutput("overworld", result(List.of(
|
||||
block(CompatAction.EXCLUDED, "cave/a"),
|
||||
block(CompatAction.EXCLUDED, "cave/b"),
|
||||
block(CompatAction.EXCLUDED, "cave/c"),
|
||||
block(CompatAction.EXCLUDED, "cave/d"))));
|
||||
|
||||
assertEquals(3, lines.size());
|
||||
assertTrue(lines.get(0), lines.get(0).contains("Pack 'overworld': content unavailable on Minecraft 26.1.2"));
|
||||
assertTrue(lines.get(1), lines.get(1).contains("minecraft:sulfur (block)"));
|
||||
assertFalse("the command prints every subject", lines.get(1).contains("more"));
|
||||
assertTrue(lines.get(1), lines.get(1).contains("cave/d"));
|
||||
assertTrue(lines.get(2), lines.get(2).contains("blockFallbacks"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compatIsInvocableWithoutAPackArgument() throws Exception {
|
||||
DirectorNodeDescriptor node = DirectorAnnotationCompatibility
|
||||
.fromMethod(CommandPack.class.getDeclaredMethod("compat", String.class))
|
||||
.orElseThrow();
|
||||
DirectorParameterDescriptor pack = node.getParameters().getFirst();
|
||||
|
||||
assertEquals("pack", pack.getName());
|
||||
assertFalse("compat must not require a pack", pack.isRequired());
|
||||
assertFalse("Director needs a non-blank default to treat the parameter as optional",
|
||||
pack.getDefaultValue().isBlank());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user