d
This commit is contained in:
Brian Neumann-Fopiano
2026-08-05 15:45:08 -06:00
parent 70d355621c
commit ce8c4ff578
18 changed files with 1958 additions and 180 deletions
@@ -19,17 +19,23 @@
package art.arcane.iris.core.commands;
import art.arcane.iris.Iris;
import art.arcane.iris.platform.bukkit.BukkitPlatform;
import art.arcane.iris.core.localization.BukkitCommandMessages;
import art.arcane.iris.core.localization.BukkitCommandMessagesExtended;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.datapack.DatapackIngestService;
import art.arcane.iris.core.service.ObjectStudioSaveService;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.IrisStructureLocator;
import art.arcane.iris.engine.framework.NativeStructureGenerationPolicy;
import art.arcane.iris.engine.platform.EngineBukkitOps;
import art.arcane.iris.engine.framework.StructureReachability;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisNativeStructureDecision;
import art.arcane.iris.engine.object.IrisRegion;
import art.arcane.iris.engine.object.NativeStructureGenerationStatus;
import art.arcane.iris.engine.platform.EngineBukkitOps;
import art.arcane.iris.platform.bukkit.BukkitPlatform;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.PlatformStructureHooks;
import art.arcane.iris.util.common.director.DirectorExecutor;
import art.arcane.iris.util.common.director.specialhandlers.ObjectHandler;
import art.arcane.iris.util.common.director.specialhandlers.StructureHandler;
@@ -40,6 +46,7 @@ import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.director.DirectorOrigin;
import art.arcane.volmlib.util.director.annotations.Director;
import art.arcane.volmlib.util.director.annotations.Param;
import art.arcane.volmlib.util.localization.MessageArgument;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
@@ -49,10 +56,14 @@ import org.bukkit.entity.Player;
import org.bukkit.generator.structure.Structure;
import org.bukkit.util.StructureSearchResult;
import art.arcane.iris.core.localization.IrisLanguage;
import art.arcane.iris.core.localization.BukkitCommandMessages;
import art.arcane.volmlib.util.localization.MessageArgument;
import art.arcane.iris.core.localization.BukkitCommandMessagesExtended;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@Director(name = "find", origin = DirectorOrigin.PLAYER, description = "Iris Find commands", descriptionKey = "iris.director.commandfind.director.iris_find_commands", aliases = "goto")
public class CommandFind implements DirectorExecutor {
@Director(description = "Find a biome", descriptionKey = "iris.director.commandfind.director.find_biome")
@@ -123,57 +134,66 @@ public class CommandFind implements DirectorExecutor {
return;
}
String structureKey = structure == null ? "" : structure.trim();
Structure nativeStructure = resolveNativeStructure(structureKey);
boolean irisReplacement = false;
if (nativeStructure != null) {
IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve(
e, structureKey, false);
if (!decision.generate()
&& decision.status() != NativeStructureGenerationStatus.REPLACED_BY_IRIS) {
commandSender.sendMessage(C.RED + NativeStructureGenerationPolicy.generationStatusMessage(
structureKey, decision.status()));
return;
}
irisReplacement = decision.status() == NativeStructureGenerationStatus.REPLACED_BY_IRIS;
if (irisReplacement && !IrisStructureLocator.hasNativePlacement(e, structureKey)) {
locateIrisStructure(e, structureKey, commandSender);
return;
}
}
if (nativeStructure == null && IrisStructureLocator.isPlaced(e, structureKey)) {
locateIrisStructure(e, structureKey, commandSender);
return;
}
if (nativeStructure == null) {
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_FIND_UNKNOWN_STRUCTURE, MessageArgument.untrusted("structureKey", structureKey)));
return;
}
final boolean replacementLocate = irisReplacement;
final boolean explicitNativePlacement = IrisStructureLocator.hasNativePlacement(
e, structureKey);
Player target = player();
if (target == null) {
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_FIND_RUN_THIS_GAME_TELEPORT_STRUCTURE));
return;
}
String structureKey = structure == null ? "" : structure.trim();
Structure nativeStructure = resolveNativeStructure(structureKey);
boolean registered = nativeStructure != null;
IrisNativeStructureDecision decision = registered
? NativeStructureGenerationPolicy.resolve(e, structureKey, false)
: null;
boolean nativePlacement = IrisStructureLocator.hasNativePlacement(e, structureKey);
boolean locatableNativePlacement = IrisStructureLocator.hasLocatableNativePlacement(e, structureKey);
boolean locatableEditablePlacement = IrisStructureLocator.hasLocatableEditablePlacement(e, structureKey);
World targetWorld = target.getWorld();
boolean nativeGenerationEnabled = targetWorld.canGenerateStructures();
boolean requiresReachability = registered && decision.generate()
&& decision.status() != NativeStructureGenerationStatus.REPLACED_BY_IRIS
&& nativeGenerationEnabled;
boolean reachable = !requiresReachability || StructureReachability.isReachable(e, structureKey);
StructureLookupRoute route = selectStructureLookupRoute(
registered, decision, nativePlacement, locatableNativePlacement,
locatableEditablePlacement, nativeGenerationEnabled, reachable);
if (route == StructureLookupRoute.UNKNOWN) {
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_FIND_UNKNOWN_STRUCTURE, MessageArgument.untrusted("structureKey", structureKey)));
return;
}
if (route == StructureLookupRoute.POLICY_DISABLED) {
commandSender.sendMessage(C.RED + NativeStructureGenerationPolicy.generationStatusMessage(
structureKey, decision.status()));
return;
}
if (route == StructureLookupRoute.NO_ACTIVE_PLACEMENT) {
commandSender.sendMessage(C.YELLOW + structureKey
+ " has no active placement in this world.");
return;
}
if (route == StructureLookupRoute.WORLD_DISABLED) {
commandSender.sendMessage(C.YELLOW + structureKey
+ " cannot generate because native structure generation is disabled for this world.");
return;
}
if (route == StructureLookupRoute.UNREACHABLE) {
KList<String> miss = StructureReachability.missingBiomeKeys(e, structureKey);
commandSender.sendMessage(C.YELLOW + structureKey
+ " cannot generate in this world (its required biomes are not produced by this pack"
+ (miss.isEmpty() ? "" : ": needs " + String.join("/", miss)) + ").");
return;
}
if (route == StructureLookupRoute.IRIS) {
locateIrisStructure(e, structureKey, commandSender);
return;
}
Location origin = target.getLocation();
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_FIND_LOCATING, MessageArgument.untrusted("structureKey", structureKey)));
J.s(() -> {
try {
if (!replacementLocate && !explicitNativePlacement
&& !StructureReachability.isReachable(e, structureKey)) {
KList<String> miss = StructureReachability.missingBiomeKeys(e, structureKey);
sendStructureMessage(target, commandSender,
C.YELLOW + structureKey + " cannot generate in this world (its required biomes are not produced by this pack"
+ (miss.isEmpty() ? "" : ": needs " + String.join("/", miss)) + ").");
return;
}
StructureSearchResult result = targetWorld.locateNearestStructure(
origin, nativeStructure, 100, false);
if (result == null || result.getLocation() == null) {
@@ -191,6 +211,365 @@ public class CommandFind implements DirectorExecutor {
});
}
@Director(description = "Print every structure excluded from /iris goto and its rejection reason to the server console", sync = true)
public void unregistered() {
VolmitSender commandSender = sender();
if (commandSender == null) {
Iris.reportError("Structure exclusion report started without a command sender context.",
new IllegalStateException("Missing command sender context"));
return;
}
Engine activeEngine = engine();
if (activeEngine == null) {
commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessages.COMMAND_FIND_NOT_IRIS_WORLD));
return;
}
Player target = player();
if (target == null) {
commandSender.sendMessage(C.RED + "Run this command from the Iris world to inspect its structures.");
return;
}
StructureExclusionReport snapshot;
try {
snapshot = collectStructureExclusions(
activeEngine, target.getWorld().canGenerateStructures());
} catch (Throwable error) {
commandSender.sendMessage(C.RED + "Could not build the structure exclusion report; see the server console.");
Iris.reportError("Could not snapshot /iris goto unregistered report for world '"
+ target.getWorld().getName() + "'.", error);
return;
}
String worldName = target.getWorld().getName();
J.a(() -> {
try {
StructureExclusionReport report = includeManagedDatapackStructures(
snapshot, DatapackIngestService.installed());
printStructureExclusionReport(worldName, report);
sendStructureMessage(target, commandSender, C.GREEN + "Printed " + report.entries().size()
+ " non-generating structure candidate(s) and their reasons to the server console. "
+ "This was an eligibility check; no chunks were searched.");
} catch (Throwable error) {
sendStructureMessage(target, commandSender,
C.RED + "Could not finish the structure exclusion report; see the server console.");
Iris.reportError("Could not finish /iris goto unregistered report for world '"
+ worldName + "'.", error);
}
});
}
private static StructureExclusionReport collectStructureExclusions(
Engine engine, boolean nativeGenerationEnabled) {
PlatformStructureHooks structureHooks = IrisPlatforms.get().structureHooks();
Map<String, String> registeredKeys = distinctStructureKeys(structureHooks.structureKeys());
Set<String> reachableKeys = StructureReachability.reachableKeys(engine);
Set<String> possibleBiomeKeys = normalizedStructureKeys(
structureHooks.possibleBiomeKeys(engine.getWorld().platformWorld()));
List<StructureExclusion> exclusions = new ArrayList<>();
int registeredExcluded = 0;
for (Map.Entry<String, String> entry : registeredKeys.entrySet()) {
String normalizedKey = entry.getKey();
String key = entry.getValue();
IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve(engine, key, false);
boolean nativePlacement = IrisStructureLocator.hasNativePlacement(engine, key);
boolean locatableNativePlacement = IrisStructureLocator.hasLocatableNativePlacement(engine, key);
boolean locatableEditablePlacement = IrisStructureLocator.hasLocatableEditablePlacement(engine, key);
StructureLookupRoute route = selectStructureLookupRoute(
true, decision, nativePlacement, locatableNativePlacement,
locatableEditablePlacement, nativeGenerationEnabled,
reachableKeys.contains(normalizedKey));
if (route == StructureLookupRoute.IRIS || route == StructureLookupRoute.NATIVE) {
continue;
}
List<String> requiredBiomes = needsNativeReachabilityReason(route, decision)
? structureHooks.structureBiomeKeys(key)
: List.of();
exclusions.add(new StructureExclusion(
StructureExclusionKind.EXCLUDED, key, null,
describeStructureExclusion(
route, key, decision.status(), nativePlacement,
requiredBiomes, possibleBiomeKeys)));
registeredExcluded++;
}
int configuredUnregistered = 0;
Set<String> configuredUnregisteredKeys = new LinkedHashSet<>();
for (String configuredKey : IrisStructureLocator.placedKeys(engine)) {
String normalizedKey = normalizeStructureKey(configuredKey);
if (normalizedKey.isEmpty()
|| registeredKeys.containsKey(normalizedKey)
|| !IrisStructureLocator.hasNativePlacement(engine, configuredKey)
|| !configuredUnregisteredKeys.add(normalizedKey)) {
continue;
}
exclusions.add(new StructureExclusion(
StructureExclusionKind.UNREGISTERED, configuredKey, null,
describeConfiguredUnregisteredNative(
IrisStructureLocator.hasLocatableNativePlacement(engine, configuredKey))));
configuredUnregistered++;
}
int editableUnplaced = 0;
Set<String> unplacedEditableKeys = new LinkedHashSet<>();
Set<String> locatableEditableKeys = normalizedStructureKeys(
IrisStructureLocator.locatableEditableKeys(engine));
for (String editableKey : engine.getData().getStructureLoader().getPossibleKeys()) {
String normalizedKey = normalizeStructureKey(editableKey);
if (!isUnplacedEditableCandidate(
normalizedKey, registeredKeys.keySet(), locatableEditableKeys,
IrisStructureLocator.hasNativePlacement(engine, editableKey))
|| !unplacedEditableKeys.add(normalizedKey)) {
continue;
}
boolean configuredPlacement = IrisStructureLocator.hasEditablePlacement(engine, editableKey);
String reason = configuredPlacement
? "editable Iris structure has a configured placement, but no matching placement is active "
+ "because its density is not positive or its Y band does not intersect this world"
: "editable Iris structure exists in this pack, but no structure placement references it";
exclusions.add(new StructureExclusion(
StructureExclusionKind.UNPLACED, editableKey, null, reason));
editableUnplaced++;
}
Set<String> configuredImportUrls = normalizedImportUrls(
engine.getDimension().getDatapackImports());
return new StructureExclusionReport(
List.copyOf(exclusions), Set.copyOf(registeredKeys.keySet()),
configuredImportUrls, registeredExcluded, configuredUnregistered,
editableUnplaced, 0);
}
static boolean isUnplacedEditableCandidate(
String normalizedKey, Set<String> registeredKeys,
Set<String> locatableEditableKeys, boolean nativePlacement) {
return normalizedKey != null
&& !normalizedKey.isEmpty()
&& !registeredKeys.contains(normalizedKey)
&& !locatableEditableKeys.contains(normalizedKey)
&& !nativePlacement;
}
private static StructureExclusionReport includeManagedDatapackStructures(
StructureExclusionReport snapshot, List<DatapackIngestService.Entry> installedEntries) {
Map<String, ManagedDatapackStructure> unregisteredStructures = new LinkedHashMap<>();
for (DatapackIngestService.Entry entry : installedEntries) {
String importUrl = normalizeImportUrl(entry.url);
if (importUrl.isEmpty() || !snapshot.configuredImportUrls().contains(importUrl)) {
continue;
}
String sourceId = entry.id == null || entry.id.isBlank() ? "unknown" : entry.id.trim();
List<String> structureKeys = entry.structureKeys == null ? List.of() : entry.structureKeys;
for (String key : structureKeys) {
String normalizedKey = normalizeStructureKey(key);
if (normalizedKey.isEmpty() || snapshot.registeredKeys().contains(normalizedKey)) {
continue;
}
ManagedDatapackStructure managed = unregisteredStructures.computeIfAbsent(
normalizedKey, ignored -> new ManagedDatapackStructure(key.trim()));
managed.sourceIds().add(sourceId);
}
}
List<StructureExclusion> entries = new ArrayList<>();
int configuredUnregistered = snapshot.configuredUnregistered();
for (StructureExclusion exclusion : snapshot.entries()) {
boolean replacedByManagedSource = exclusion.kind() == StructureExclusionKind.UNREGISTERED
&& unregisteredStructures.containsKey(normalizeStructureKey(exclusion.key()));
if (replacedByManagedSource) {
configuredUnregistered--;
continue;
}
entries.add(exclusion);
}
for (ManagedDatapackStructure managed : unregisteredStructures.values()) {
entries.add(new StructureExclusion(
StructureExclusionKind.UNREGISTERED, managed.key(),
String.join(",", managed.sourceIds()),
"declared by an Iris-managed datapack but absent from the live registry; "
+ "restart, enablement, or datapack validation may be required"));
}
sortStructureExclusions(entries);
return new StructureExclusionReport(
List.copyOf(entries), snapshot.registeredKeys(), snapshot.configuredImportUrls(),
snapshot.registeredExcluded(), Math.max(0, configuredUnregistered),
snapshot.editableUnplaced(), unregisteredStructures.size());
}
private static void printStructureExclusionReport(
String worldName, StructureExclusionReport report) {
Iris.info("Iris goto unregistered report for world '%s': %d non-generating structure candidate(s).",
worldName, report.entries().size());
for (StructureExclusion exclusion : report.entries()) {
String source = exclusion.source() == null
? ""
: " (source " + exclusion.source() + ")";
Iris.info("[%s] %s%s: %s",
exclusion.kind().label(), exclusion.key(), source, exclusion.reason());
}
Iris.info("Iris goto unregistered summary: %d registered key(s) excluded, "
+ "%d managed datapack key(s) unregistered, %d configured native key(s) unregistered, "
+ "%d editable Iris structure(s) unplaced.",
report.registeredExcluded(), report.managedUnregistered(),
report.configuredUnregistered(), report.editableUnplaced());
Iris.info("Eligibility report only; no chunks were searched and existing generated starts were not scanned.");
}
private static boolean needsNativeReachabilityReason(
StructureLookupRoute route, IrisNativeStructureDecision decision) {
return route == StructureLookupRoute.UNREACHABLE
|| route == StructureLookupRoute.NO_ACTIVE_PLACEMENT && decision.generate();
}
static String describeStructureExclusion(
StructureLookupRoute route, String key, NativeStructureGenerationStatus status,
boolean nativePlacement, List<String> requiredBiomes, Set<String> possibleBiomeKeys) {
return switch (route) {
case UNKNOWN -> "the key is not registered by the active server/datapack";
case POLICY_DISABLED -> NativeStructureGenerationPolicy.generationStatusMessage(key, status);
case WORLD_DISABLED -> "native structure generation is disabled for this world";
case UNREACHABLE -> nativeReachabilityReason(requiredBiomes, possibleBiomeKeys);
case NO_ACTIVE_PLACEMENT -> {
String placementType = nativePlacement
? "configured nativeStructures placement"
: "configured Iris replacement";
String reason = placementType + " is inactive: no matching placement has positive density "
+ "when density-based and a Y band intersecting this world";
if (status == NativeStructureGenerationStatus.GENERATE_NATIVE) {
reason += "; the native route is also inactive because "
+ nativeReachabilityReason(requiredBiomes, possibleBiomeKeys);
}
yield reason;
}
case IRIS, NATIVE -> throw new IllegalArgumentException(
"Active structure route cannot be described as excluded: " + route);
};
}
static String nativeReachabilityReason(
List<String> requiredBiomes, Set<String> possibleBiomeKeys) {
if (requiredBiomes == null || requiredBiomes.isEmpty()) {
return "its resolved biome filter is empty";
}
Set<String> possible = possibleBiomeKeys == null ? Set.of() : possibleBiomeKeys;
for (String requiredBiome : requiredBiomes) {
if (possible.contains(normalizeStructureKey(requiredBiome))) {
return "no active positive-weight, positive-frequency structure-set entry includes it in this world";
}
}
return "this pack does not produce any of its required biome(s): "
+ String.join("/", requiredBiomes);
}
static String describeConfiguredUnregisteredNative(boolean locatablePlacement) {
String reason = "configured in nativeStructures, but the key is not registered by the active "
+ "server/datapack, so Minecraft cannot create its native structure start";
return locatablePlacement
? reason
: reason + "; its Iris placement is also inactive because no matching placement has positive "
+ "density when density-based and a Y band intersecting this world";
}
private static Map<String, String> distinctStructureKeys(List<String> keys) {
Map<String, String> distinctKeys = new LinkedHashMap<>();
for (String key : keys) {
String normalizedKey = normalizeStructureKey(key);
if (!normalizedKey.isEmpty()) {
distinctKeys.putIfAbsent(normalizedKey, key.trim());
}
}
return distinctKeys;
}
private static Set<String> normalizedStructureKeys(Iterable<String> keys) {
Set<String> normalizedKeys = new LinkedHashSet<>();
if (keys == null) {
return normalizedKeys;
}
for (String key : keys) {
String normalizedKey = normalizeStructureKey(key);
if (!normalizedKey.isEmpty()) {
normalizedKeys.add(normalizedKey);
}
}
return normalizedKeys;
}
private static Set<String> normalizedImportUrls(Iterable<String> urls) {
Set<String> normalizedUrls = new LinkedHashSet<>();
if (urls == null) {
return normalizedUrls;
}
for (String url : urls) {
String normalizedUrl = normalizeImportUrl(url);
if (!normalizedUrl.isEmpty()) {
normalizedUrls.add(normalizedUrl);
}
}
return Set.copyOf(normalizedUrls);
}
private static String normalizeImportUrl(String url) {
return url == null ? "" : url.trim();
}
private static void sortStructureExclusions(List<StructureExclusion> exclusions) {
exclusions.sort((left, right) -> {
int kindComparison = left.kind().compareTo(right.kind());
return kindComparison == 0
? left.key().compareToIgnoreCase(right.key())
: kindComparison;
});
}
private static String normalizeStructureKey(String key) {
return key == null ? "" : key.trim().toLowerCase(Locale.ROOT);
}
static StructureLookupRoute selectStructureLookupRoute(
boolean registered, IrisNativeStructureDecision decision, boolean nativePlacement,
boolean locatableNativePlacement, boolean locatableEditablePlacement,
boolean nativeGenerationEnabled, boolean reachable) {
if (!registered) {
return !nativePlacement && locatableEditablePlacement
? StructureLookupRoute.IRIS
: StructureLookupRoute.UNKNOWN;
}
if (decision == null) {
throw new IllegalArgumentException("Registered structure lookup requires a generation decision");
}
if (!decision.generate()
&& decision.status() != NativeStructureGenerationStatus.REPLACED_BY_IRIS) {
return StructureLookupRoute.POLICY_DISABLED;
}
if (decision.status() == NativeStructureGenerationStatus.REPLACED_BY_IRIS) {
if (!nativePlacement) {
return locatableEditablePlacement
? StructureLookupRoute.IRIS
: StructureLookupRoute.NO_ACTIVE_PLACEMENT;
}
if (!nativeGenerationEnabled) {
return StructureLookupRoute.WORLD_DISABLED;
}
return locatableNativePlacement
? StructureLookupRoute.NATIVE
: StructureLookupRoute.NO_ACTIVE_PLACEMENT;
}
if (!nativeGenerationEnabled) {
return StructureLookupRoute.WORLD_DISABLED;
}
if (nativePlacement && locatableNativePlacement) {
return StructureLookupRoute.NATIVE;
}
if (reachable) {
return StructureLookupRoute.NATIVE;
}
return nativePlacement
? StructureLookupRoute.NO_ACTIVE_PLACEMENT
: StructureLookupRoute.UNREACHABLE;
}
private static Structure resolveNativeStructure(String structureKey) {
Registry<Structure> structureRegistry = Bukkit.getRegistry(Structure.class);
if (structureRegistry == null) {
@@ -314,4 +693,46 @@ public class CommandFind implements DirectorExecutor {
private void sendStructureMessage(Player target, VolmitSender commandSender, String message) {
J.runEntity(target, () -> commandSender.sendMessage(message));
}
enum StructureLookupRoute {
UNKNOWN,
POLICY_DISABLED,
NO_ACTIVE_PLACEMENT,
WORLD_DISABLED,
UNREACHABLE,
IRIS,
NATIVE
}
private enum StructureExclusionKind {
UNREGISTERED("unregistered"),
EXCLUDED("excluded"),
UNPLACED("unplaced");
private final String label;
StructureExclusionKind(String label) {
this.label = label;
}
private String label() {
return label;
}
}
private record StructureExclusion(
StructureExclusionKind kind, String key, String source, String reason) {
}
private record StructureExclusionReport(
List<StructureExclusion> entries, Set<String> registeredKeys,
Set<String> configuredImportUrls, int registeredExcluded,
int configuredUnregistered, int editableUnplaced, int managedUnregistered) {
}
private record ManagedDatapackStructure(String key, Set<String> sourceIds) {
private ManagedDatapackStructure(String key) {
this(key, new LinkedHashSet<>());
}
}
}
@@ -1,12 +1,15 @@
package art.arcane.iris.core.commands;
import art.arcane.iris.engine.framework.NativeStructureGenerationPolicy;
import art.arcane.iris.engine.object.IrisNativeStructureDecision;
import art.arcane.iris.engine.object.NativeStructureGenerationStatus;
import org.junit.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -25,32 +28,189 @@ public class IrisStructureLocateCommandContractTest {
}
@Test
public void findRoutesRegisteredReplacementThroughPersistedNativeLocate() throws IOException {
public void findRoutesOnlyActiveWorldStructures() throws IOException {
String source = Files.readString(Path.of(System.getProperty("iris.commandFindSource")));
int methodStart = source.indexOf("public void structure(");
int methodEnd = source.indexOf("private static Structure resolveNativeStructure", methodStart);
int methodEnd = source.indexOf("static StructureLookupRoute selectStructureLookupRoute", methodStart);
String method = source.substring(methodStart, methodEnd);
int nativeResolution = method.indexOf("resolveNativeStructure(structureKey)");
int policyResolution = method.indexOf("NativeStructureGenerationPolicy.resolve(", nativeResolution);
int replacementCheck = method.indexOf(
"decision.status() == NativeStructureGenerationStatus.REPLACED_BY_IRIS", policyResolution);
int genericIrisLookup = method.indexOf(
"nativeStructure == null && IrisStructureLocator.isPlaced(e, structureKey)", replacementCheck);
int replacementLocate = method.indexOf("final boolean replacementLocate = irisReplacement", genericIrisLookup);
int nativeLocate = method.indexOf("targetWorld.locateNearestStructure(", genericIrisLookup);
int nativeLocatableCheck = method.indexOf(
"IrisStructureLocator.hasLocatableNativePlacement(e, structureKey)", policyResolution);
int editableLocatableCheck = method.indexOf(
"IrisStructureLocator.hasLocatableEditablePlacement(e, structureKey)", nativeLocatableCheck);
int worldGenerationCheck = method.indexOf("targetWorld.canGenerateStructures()", editableLocatableCheck);
int routeResolution = method.indexOf("selectStructureLookupRoute(", editableLocatableCheck);
int nativeLocate = method.indexOf("targetWorld.locateNearestStructure(", routeResolution);
assertTrue(nativeResolution >= 0);
assertTrue(policyResolution > nativeResolution);
assertTrue(replacementCheck > policyResolution);
assertTrue(genericIrisLookup > replacementCheck);
assertTrue(replacementLocate > genericIrisLookup);
assertTrue(nativeLocate > policyResolution);
assertTrue(method.contains("irisReplacement && !IrisStructureLocator.hasNativePlacement"));
assertTrue(method.contains("!replacementLocate && !explicitNativePlacement"));
assertTrue(method.contains("&& !StructureReachability.isReachable"));
assertTrue(method.contains("decision.status() != NativeStructureGenerationStatus.REPLACED_BY_IRIS"));
assertTrue(nativeLocatableCheck > policyResolution);
assertTrue(editableLocatableCheck > nativeLocatableCheck);
assertTrue(worldGenerationCheck > editableLocatableCheck);
assertTrue(routeResolution > editableLocatableCheck);
assertTrue(nativeLocate > routeResolution);
assertFalse(method.contains("IrisStructureLocator.isPlaced(e, structureKey)"));
assertFalse(method.contains("NativeStructureLocateCapability"));
}
@Test
public void findRejectsUnregisteredNativeAndDormantPlacements() {
assertEquals(CommandFind.StructureLookupRoute.UNKNOWN,
CommandFind.selectStructureLookupRoute(
false, null, true, true, false, true, true));
assertEquals(CommandFind.StructureLookupRoute.UNKNOWN,
CommandFind.selectStructureLookupRoute(
false, null, false, false, false, true, true));
assertEquals(CommandFind.StructureLookupRoute.NO_ACTIVE_PLACEMENT,
CommandFind.selectStructureLookupRoute(
true, decision(NativeStructureGenerationStatus.REPLACED_BY_IRIS),
false, false, false, true, true));
assertEquals(CommandFind.StructureLookupRoute.NO_ACTIVE_PLACEMENT,
CommandFind.selectStructureLookupRoute(
true, decision(NativeStructureGenerationStatus.GENERATE_NATIVE),
true, false, false, true, false));
}
@Test
public void findAllowsLocatableEditablePlacementsWithoutNativeGeneration() {
assertEquals(CommandFind.StructureLookupRoute.IRIS,
CommandFind.selectStructureLookupRoute(
false, null, false, false, true, false, false));
assertEquals(CommandFind.StructureLookupRoute.IRIS,
CommandFind.selectStructureLookupRoute(
true, decision(NativeStructureGenerationStatus.REPLACED_BY_IRIS),
false, false, true, false, false));
}
@Test
public void findRequiresWorldGenerationAndReachabilityForNativeRoutes() {
assertEquals(CommandFind.StructureLookupRoute.WORLD_DISABLED,
CommandFind.selectStructureLookupRoute(
true, decision(NativeStructureGenerationStatus.REPLACED_BY_IRIS),
true, true, false, false, true));
assertEquals(CommandFind.StructureLookupRoute.WORLD_DISABLED,
CommandFind.selectStructureLookupRoute(
true, decision(NativeStructureGenerationStatus.GENERATE_NATIVE),
false, false, false, false, true));
assertEquals(CommandFind.StructureLookupRoute.UNREACHABLE,
CommandFind.selectStructureLookupRoute(
true, decision(NativeStructureGenerationStatus.GENERATE_NATIVE),
false, false, false, true, false));
assertEquals(CommandFind.StructureLookupRoute.NATIVE,
CommandFind.selectStructureLookupRoute(
true, decision(NativeStructureGenerationStatus.GENERATE_NATIVE),
true, true, false, true, false));
assertEquals(CommandFind.StructureLookupRoute.NATIVE,
CommandFind.selectStructureLookupRoute(
true, decision(NativeStructureGenerationStatus.GENERATE_NATIVE),
true, false, false, true, true));
}
@Test
public void findHonorsPackDisableBeforePlacementRouting() {
assertEquals(CommandFind.StructureLookupRoute.POLICY_DISABLED,
CommandFind.selectStructureLookupRoute(
true, decision(NativeStructureGenerationStatus.DISABLED_BY_PACK),
false, false, true, true, true));
}
@Test
public void unregisteredDiagnosticExplainsEveryRejectedRoute() {
assertEquals(
"Native structure minecraft:village is disabled by this dimension's importedStructures settings.",
CommandFind.describeStructureExclusion(
CommandFind.StructureLookupRoute.POLICY_DISABLED, "minecraft:village",
NativeStructureGenerationStatus.DISABLED_BY_PACK,
false, List.of(), Set.of()));
assertEquals(
"native structure generation is disabled for this world",
CommandFind.describeStructureExclusion(
CommandFind.StructureLookupRoute.WORLD_DISABLED, "minecraft:village",
NativeStructureGenerationStatus.GENERATE_NATIVE,
false, List.of(), Set.of()));
assertEquals(
"its resolved biome filter is empty",
CommandFind.describeStructureExclusion(
CommandFind.StructureLookupRoute.UNREACHABLE, "towns_and_towers:exclusive",
NativeStructureGenerationStatus.GENERATE_NATIVE,
false, List.of(), Set.of()));
assertEquals(
"this pack does not produce any of its required biome(s): terralith:alpine_grove/bwg:aspen_boreal",
CommandFind.describeStructureExclusion(
CommandFind.StructureLookupRoute.UNREACHABLE, "towns_and_towers:exclusive",
NativeStructureGenerationStatus.GENERATE_NATIVE,
false, List.of("terralith:alpine_grove", "bwg:aspen_boreal"),
Set.of("minecraft:plains")));
assertEquals(
"no active positive-weight, positive-frequency structure-set entry includes it in this world",
CommandFind.describeStructureExclusion(
CommandFind.StructureLookupRoute.UNREACHABLE, "example:dormant",
NativeStructureGenerationStatus.GENERATE_NATIVE,
false, List.of("minecraft:plains"), Set.of("minecraft:plains")));
assertEquals(
"no active positive-weight, positive-frequency structure-set entry includes it in this world",
CommandFind.describeStructureExclusion(
CommandFind.StructureLookupRoute.UNREACHABLE, "example:partial_overlap",
NativeStructureGenerationStatus.GENERATE_NATIVE,
false, List.of("minecraft:plains", "mod:absent"), Set.of("minecraft:plains")));
}
@Test
public void unregisteredDiagnosticExplainsInactiveAndMissingNativePlacements() {
assertEquals(
"configured Iris replacement is inactive: no matching placement has positive density when "
+ "density-based and a Y band intersecting this world",
CommandFind.describeStructureExclusion(
CommandFind.StructureLookupRoute.NO_ACTIVE_PLACEMENT, "example:replacement",
NativeStructureGenerationStatus.REPLACED_BY_IRIS,
false, List.of(), Set.of()));
assertEquals(
"configured nativeStructures placement is inactive: no matching placement has positive density "
+ "when density-based and a Y band intersecting this world; the native route is also "
+ "inactive because its resolved biome filter is empty",
CommandFind.describeStructureExclusion(
CommandFind.StructureLookupRoute.NO_ACTIVE_PLACEMENT, "example:native",
NativeStructureGenerationStatus.GENERATE_NATIVE,
true, List.of(), Set.of()));
assertTrue(CommandFind.describeConfiguredUnregisteredNative(true)
.contains("key is not registered by the active server/datapack"));
assertFalse(CommandFind.describeConfiguredUnregisteredNative(true)
.contains("Iris placement is also inactive"));
assertTrue(CommandFind.describeConfiguredUnregisteredNative(false)
.contains("Iris placement is also inactive"));
}
@Test
public void unplacedEditableDiagnosticDefersToRegistryAndNativeCategories() {
assertTrue(CommandFind.isUnplacedEditableCandidate(
"pack:editable", Set.of(), Set.of(), false));
assertFalse(CommandFind.isUnplacedEditableCandidate(
"pack:editable", Set.of("pack:editable"), Set.of(), false));
assertFalse(CommandFind.isUnplacedEditableCandidate(
"pack:editable", Set.of(), Set.of("pack:editable"), false));
assertFalse(CommandFind.isUnplacedEditableCandidate(
"pack:editable", Set.of(), Set.of(), true));
}
@Test
public void unregisteredDiagnosticPrintsDetailsOnlyToConsole() throws IOException {
String source = Files.readString(Path.of(System.getProperty("iris.commandFindSource")));
int methodStart = source.indexOf("public void unregistered()");
int methodEnd = source.indexOf("private static StructureExclusionReport", methodStart);
String method = source.substring(methodStart, methodEnd);
assertTrue(methodStart >= 0);
assertTrue(source.contains("Iris.info(\"[%s] %s%s: %s\""));
assertTrue(method.contains("non-generating structure candidate(s) and their reasons to the server console"));
assertTrue(method.contains("no chunks were searched"));
assertTrue(method.indexOf("DatapackIngestService.installed()") > method.indexOf("J.a(() ->"));
assertTrue(source.contains("engine.getDimension().getDatapackImports()"));
assertTrue(source.contains("snapshot.configuredImportUrls().contains(importUrl)"));
assertTrue(source.contains("engine.getData().getStructureLoader().getPossibleKeys()"));
assertFalse(method.contains("commandSender.sendMessage(exclusion"));
}
@Test
public void findNativePolicyMessagesMatchModdedDiagnostics() {
assertEquals(
@@ -95,4 +255,9 @@ public class IrisStructureLocateCommandContractTest {
assertTrue(reachabilityGuard > nativeRequirement);
assertTrue(reachabilityLookup > reachabilityGuard);
}
private static IrisNativeStructureDecision decision(NativeStructureGenerationStatus status) {
return new IrisNativeStructureDecision(
status, 0, null, false, false, null, null);
}
}