mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-27 12:41:43 +00:00
d
d
This commit is contained in:
@@ -69,9 +69,14 @@ public final class IrisStructureLocator {
|
||||
private static final Pattern NAMESPACED_RESOURCE_KEY = Pattern.compile("[a-z0-9_.-]+:[a-z0-9/._-]+");
|
||||
|
||||
private static final Cache<Engine, PlacementIndex> INDEX_CACHE = Caffeine.newBuilder().weakKeys().build();
|
||||
private static final Cache<Engine, LocatableIndex> LOCATABLE_INDEX_CACHE =
|
||||
Caffeine.newBuilder().weakKeys().build();
|
||||
private static final PlacementIndex EMPTY_INDEX = new PlacementIndex(
|
||||
Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(),
|
||||
Collections.emptySet(), Collections.emptyList());
|
||||
private static final LocatableIndex EMPTY_LOCATABLE_INDEX = new LocatableIndex(
|
||||
Collections.emptySet(), Collections.emptySet(), Collections.emptySet(),
|
||||
Collections.emptySet(), Collections.emptySet(), Collections.emptySet());
|
||||
private static final LocateResult NOT_FOUND_RESULT = new LocateResult(LocateStatus.NOT_FOUND, 0, 0, 0);
|
||||
private static final LocateResult SEARCH_LIMIT_RESULT =
|
||||
new LocateResult(LocateStatus.SEARCH_LIMIT_REACHED, 0, 0, 0);
|
||||
@@ -97,6 +102,39 @@ public final class IrisStructureLocator {
|
||||
|| placementIndex.vanillaAliases.contains(normalizedKey);
|
||||
}
|
||||
|
||||
public static boolean hasLocatablePlacement(Engine engine, String key) {
|
||||
if (engine == null || key == null || key.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
return locatableIndex(engine).normalizedKeys().contains(normalize(key));
|
||||
}
|
||||
|
||||
public static boolean hasLocatableEditablePlacement(Engine engine, String key) {
|
||||
if (engine == null || key == null || key.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
return locatableIndex(engine).normalizedEditableKeys().contains(normalize(key));
|
||||
}
|
||||
|
||||
public static boolean hasLocatableNativePlacement(Engine engine, String key) {
|
||||
if (engine == null || key == null || key.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
return locatableIndex(engine).normalizedNativeKeys().contains(normalize(key));
|
||||
}
|
||||
|
||||
public static Set<String> locatableKeys(Engine engine) {
|
||||
return locatableIndex(engine).keys();
|
||||
}
|
||||
|
||||
public static Set<String> locatableEditableKeys(Engine engine) {
|
||||
return locatableIndex(engine).editableKeys();
|
||||
}
|
||||
|
||||
public static Set<String> locatableNativeKeys(Engine engine) {
|
||||
return locatableIndex(engine).nativeKeys();
|
||||
}
|
||||
|
||||
public static boolean hasNativePlacement(Engine engine, String key) {
|
||||
if (engine == null || key == null || key.isBlank()) {
|
||||
return false;
|
||||
@@ -126,6 +164,7 @@ public final class IrisStructureLocator {
|
||||
public static void invalidate(Engine engine) {
|
||||
if (engine != null) {
|
||||
INDEX_CACHE.invalidate(engine);
|
||||
LOCATABLE_INDEX_CACHE.invalidate(engine);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -796,7 +835,8 @@ public final class IrisStructureLocator {
|
||||
List<IrisStructurePlacement> concentricRings = new ArrayList<>();
|
||||
boolean hasDensity = false;
|
||||
for (IrisStructurePlacement placement : index(engine).placements) {
|
||||
if (!matches(placement, key, engine.getData())) {
|
||||
if (!isSearchablePlacement(engine, placement)
|
||||
|| !matches(placement, key, engine.getData())) {
|
||||
continue;
|
||||
}
|
||||
if (placement.getDistribution() == StructureDistribution.RANDOM_SPREAD) {
|
||||
@@ -805,7 +845,7 @@ public final class IrisStructureLocator {
|
||||
StructurePlacementGrid.placementSalt(placement)));
|
||||
} else if (placement.getDistribution() == StructureDistribution.CONCENTRIC_RINGS) {
|
||||
concentricRings.add(placement);
|
||||
} else if (isSearchableDensityPlacement(engine, placement)) {
|
||||
} else if (placement.getDistribution() == StructureDistribution.DENSITY) {
|
||||
hasDensity = true;
|
||||
}
|
||||
}
|
||||
@@ -813,8 +853,18 @@ public final class IrisStructureLocator {
|
||||
}
|
||||
|
||||
static boolean isSearchableDensityPlacement(Engine engine, IrisStructurePlacement placement) {
|
||||
if (engine == null || placement == null || placement.getDistribution() != StructureDistribution.DENSITY
|
||||
|| !(placement.getDensity() > 0.0) || engine.getHeight() <= 0) {
|
||||
return placement != null
|
||||
&& placement.getDistribution() == StructureDistribution.DENSITY
|
||||
&& isSearchablePlacement(engine, placement);
|
||||
}
|
||||
|
||||
static boolean isSearchablePlacement(Engine engine, IrisStructurePlacement placement) {
|
||||
if (engine == null || placement == null || placement.getDistribution() == null
|
||||
|| engine.getHeight() <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (placement.getDistribution() == StructureDistribution.DENSITY
|
||||
&& !(placement.getDensity() > 0.0)) {
|
||||
return false;
|
||||
}
|
||||
long worldMin = (long) engine.getMinHeight() + (placement.isUnderground() ? 1L : 0L);
|
||||
@@ -952,6 +1002,64 @@ public final class IrisStructureLocator {
|
||||
return INDEX_CACHE.get(engine, ignored -> build(engine));
|
||||
}
|
||||
|
||||
private static LocatableIndex locatableIndex(Engine engine) {
|
||||
if (engine == null) {
|
||||
return EMPTY_LOCATABLE_INDEX;
|
||||
}
|
||||
return LOCATABLE_INDEX_CACHE.get(engine, ignored -> buildLocatableIndex(engine));
|
||||
}
|
||||
|
||||
private static LocatableIndex buildLocatableIndex(Engine engine) {
|
||||
IrisData data = engine.getData();
|
||||
Set<String> keys = new LinkedHashSet<>();
|
||||
Set<String> normalizedKeys = new LinkedHashSet<>();
|
||||
Set<String> editableKeys = new LinkedHashSet<>();
|
||||
Set<String> normalizedEditableKeys = new LinkedHashSet<>();
|
||||
Set<String> nativeKeys = new LinkedHashSet<>();
|
||||
Set<String> normalizedNativeKeys = new LinkedHashSet<>();
|
||||
for (IrisStructurePlacement placement : index(engine).placements) {
|
||||
if (!isSearchablePlacement(engine, placement)) {
|
||||
continue;
|
||||
}
|
||||
if (placement.hasNativeStructures()) {
|
||||
for (IrisNativeStructure source : placement.getNativeStructures()) {
|
||||
addLocatableKey(source.getStructure(), keys, normalizedKeys);
|
||||
addLocatableKey(source.getStructure(), nativeKeys, normalizedNativeKeys);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (String structureKey : placement.getStructures()) {
|
||||
IrisStructure structure = data.load(IrisStructure.class, structureKey, false);
|
||||
if (structure == null) {
|
||||
throw new IllegalStateException(
|
||||
"Iris structure placement references missing structure '" + structureKey + "'");
|
||||
}
|
||||
addLocatableKey(structureKey, keys, normalizedKeys);
|
||||
addLocatableKey(structureKey, editableKeys, normalizedEditableKeys);
|
||||
addLocatableKey(structure.getLoadKey(), keys, normalizedKeys);
|
||||
addLocatableKey(structure.getLoadKey(), editableKeys, normalizedEditableKeys);
|
||||
addLocatableKey(structure.getVanillaSource(), keys, normalizedKeys);
|
||||
addLocatableKey(structure.getVanillaSource(), editableKeys, normalizedEditableKeys);
|
||||
}
|
||||
}
|
||||
return new LocatableIndex(
|
||||
Collections.unmodifiableSet(keys),
|
||||
Collections.unmodifiableSet(normalizedKeys),
|
||||
Collections.unmodifiableSet(editableKeys),
|
||||
Collections.unmodifiableSet(normalizedEditableKeys),
|
||||
Collections.unmodifiableSet(nativeKeys),
|
||||
Collections.unmodifiableSet(normalizedNativeKeys));
|
||||
}
|
||||
|
||||
private static void addLocatableKey(String key, Set<String> keys, Set<String> normalizedKeys) {
|
||||
if (key == null || key.isBlank()) {
|
||||
return;
|
||||
}
|
||||
String trimmedKey = key.trim();
|
||||
keys.add(trimmedKey);
|
||||
normalizedKeys.add(normalize(trimmedKey));
|
||||
}
|
||||
|
||||
private static PlacementIndex build(Engine engine) {
|
||||
IrisData data = engine.getData();
|
||||
Set<String> loadKeys = new LinkedHashSet<>();
|
||||
@@ -1112,6 +1220,11 @@ public final class IrisStructureLocator {
|
||||
List<IrisStructurePlacement> concentricRings, boolean hasDensity) {
|
||||
}
|
||||
|
||||
private record LocatableIndex(Set<String> keys, Set<String> normalizedKeys,
|
||||
Set<String> editableKeys, Set<String> normalizedEditableKeys,
|
||||
Set<String> nativeKeys, Set<String> normalizedNativeKeys) {
|
||||
}
|
||||
|
||||
private record ResolvedStart(int originX, int baseY, int originZ) {
|
||||
}
|
||||
|
||||
|
||||
+88
-30
@@ -18,42 +18,57 @@
|
||||
|
||||
package art.arcane.iris.util.common.director.specialhandlers;
|
||||
|
||||
import art.arcane.iris.core.nms.INMS;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.framework.IrisStructureLocator;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.iris.engine.framework.NativeStructureGenerationPolicy;
|
||||
import art.arcane.iris.engine.framework.StructureReachability;
|
||||
import art.arcane.iris.engine.object.IrisNativeStructureDecision;
|
||||
import art.arcane.iris.engine.object.NativeStructureGenerationStatus;
|
||||
import art.arcane.iris.spi.IrisPlatforms;
|
||||
import art.arcane.iris.spi.PlatformStructureHooks;
|
||||
import art.arcane.iris.util.common.director.DirectorParameterHandler;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.director.exceptions.DirectorParsingException;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class StructureHandler implements DirectorParameterHandler<String> {
|
||||
@Override
|
||||
public KList<String> getPossibilities() {
|
||||
KList<String> keys = new KList<>();
|
||||
|
||||
try {
|
||||
for (String k : INMS.get().getStructureKeys()) {
|
||||
if (k != null && !k.isEmpty()) {
|
||||
keys.addIfMissing(k);
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
Engine activeEngine = engine();
|
||||
if (activeEngine == null) {
|
||||
return new KList<>();
|
||||
}
|
||||
|
||||
try {
|
||||
Engine e = engine();
|
||||
if (e != null) {
|
||||
for (String k : IrisStructureLocator.placedKeys(e)) {
|
||||
if (k != null && !k.isEmpty()) {
|
||||
keys.addIfMissing(k);
|
||||
}
|
||||
}
|
||||
boolean nativeGenerationEnabled = nativeStructureGenerationEnabled();
|
||||
PlatformStructureHooks structureHooks = IrisPlatforms.get().structureHooks();
|
||||
Map<String, String> registeredKeys = distinctKeys(structureHooks.structureKeys());
|
||||
Set<String> reachableKeys = StructureReachability.reachableKeys(activeEngine);
|
||||
Map<String, String> suggestions = new LinkedHashMap<>();
|
||||
|
||||
for (Map.Entry<String, String> entry : registeredKeys.entrySet()) {
|
||||
if (isEligibleRegisteredKey(activeEngine, entry.getValue(), entry.getKey(), reachableKeys,
|
||||
nativeGenerationEnabled)) {
|
||||
suggestions.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
return keys;
|
||||
Set<String> locatableKeys = IrisStructureLocator.locatableEditableKeys(activeEngine);
|
||||
for (String key : locatableKeys) {
|
||||
String normalizedKey = normalizeKey(key);
|
||||
if (!normalizedKey.isEmpty()
|
||||
&& !registeredKeys.containsKey(normalizedKey)
|
||||
&& !IrisStructureLocator.hasNativePlacement(activeEngine, key)) {
|
||||
suggestions.putIfAbsent(normalizedKey, key.trim());
|
||||
}
|
||||
}
|
||||
|
||||
return new KList<>(suggestions.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,15 +79,12 @@ public class StructureHandler implements DirectorParameterHandler<String> {
|
||||
@Override
|
||||
public String parse(String in, boolean force) throws DirectorParsingException {
|
||||
KList<String> options = getPossibilities(in);
|
||||
|
||||
if (options.isEmpty()) {
|
||||
return in;
|
||||
}
|
||||
try {
|
||||
return options.stream().filter((i) -> toString(i).equalsIgnoreCase(in)).collect(Collectors.toList()).get(0);
|
||||
} catch (Throwable e) {
|
||||
return in;
|
||||
for (String option : options) {
|
||||
if (option.equalsIgnoreCase(in)) {
|
||||
return option;
|
||||
}
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,4 +98,50 @@ public class StructureHandler implements DirectorParameterHandler<String> {
|
||||
|
||||
return f == null ? "minecraft_ancient_city" : f;
|
||||
}
|
||||
|
||||
protected boolean nativeStructureGenerationEnabled() {
|
||||
Player activePlayer = player();
|
||||
return activePlayer != null && activePlayer.getWorld().canGenerateStructures();
|
||||
}
|
||||
|
||||
private static Map<String, String> distinctKeys(List<String> keys) {
|
||||
Map<String, String> distinct = new LinkedHashMap<>();
|
||||
for (String key : keys) {
|
||||
String normalizedKey = normalizeKey(key);
|
||||
if (!normalizedKey.isEmpty()) {
|
||||
distinct.putIfAbsent(normalizedKey, key.trim());
|
||||
}
|
||||
}
|
||||
return distinct;
|
||||
}
|
||||
|
||||
private static boolean isEligibleRegisteredKey(Engine engine, String key, String normalizedKey,
|
||||
Set<String> reachableKeys, boolean nativeGenerationEnabled) {
|
||||
IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve(engine, key, false);
|
||||
boolean nativePlacement = IrisStructureLocator.hasNativePlacement(engine, key);
|
||||
boolean locatableNativePlacement = nativePlacement
|
||||
&& IrisStructureLocator.hasLocatableNativePlacement(engine, key);
|
||||
boolean locatableEditableReplacement = !nativePlacement
|
||||
&& decision.status() == NativeStructureGenerationStatus.REPLACED_BY_IRIS
|
||||
&& IrisStructureLocator.hasLocatableEditablePlacement(engine, key);
|
||||
return isEligibleRegisteredKey(
|
||||
decision, nativePlacement, locatableNativePlacement, locatableEditableReplacement,
|
||||
reachableKeys.contains(normalizedKey), nativeGenerationEnabled);
|
||||
}
|
||||
|
||||
static boolean isEligibleRegisteredKey(IrisNativeStructureDecision decision, boolean nativePlacement,
|
||||
boolean locatableNativePlacement,
|
||||
boolean locatableEditableReplacement,
|
||||
boolean reachable, boolean nativeGenerationEnabled) {
|
||||
if (decision.status() == NativeStructureGenerationStatus.REPLACED_BY_IRIS) {
|
||||
return locatableEditableReplacement
|
||||
|| nativeGenerationEnabled && nativePlacement && locatableNativePlacement;
|
||||
}
|
||||
return nativeGenerationEnabled && decision.generate()
|
||||
&& (reachable || (nativePlacement && locatableNativePlacement));
|
||||
}
|
||||
|
||||
private static String normalizeKey(String key) {
|
||||
return key == null ? "" : key.trim().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
+64
@@ -77,6 +77,70 @@ public class IrisStructureLocatorContractTest {
|
||||
assertFalse(IrisStructureLocator.isPlaced(engine, ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void locatablePlacementsExcludeDisabledDensityConfigurations() {
|
||||
Engine disabled = densityEngine(0.0, false, -64, 384, -2032, 2032);
|
||||
Engine active = densityEngine(0.01, false, -64, 384, -2032, 2032);
|
||||
|
||||
assertTrue(IrisStructureLocator.isPlaced(disabled, "test:density"));
|
||||
assertFalse(IrisStructureLocator.hasLocatablePlacement(disabled, "test:density"));
|
||||
assertTrue(IrisStructureLocator.locatableKeys(disabled).isEmpty());
|
||||
assertTrue(IrisStructureLocator.hasLocatablePlacement(active, "test:density"));
|
||||
assertTrue(IrisStructureLocator.locatableKeys(active).contains("test:density"));
|
||||
assertFalse(IrisStructureLocator.hasLocatablePlacement(null, "test:density"));
|
||||
assertTrue(IrisStructureLocator.locatableKeys(null).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void locatablePlacementsExcludeEveryDistributionOutsideWorldHeight() {
|
||||
Engine randomSpread = densityEngine(1.0, false, -64, 384, 400, 500);
|
||||
randomSpread.getDimension().getStructures().get(0)
|
||||
.setDistribution(StructureDistribution.RANDOM_SPREAD);
|
||||
Engine concentricRings = densityEngine(1.0, false, -64, 384, 400, 500);
|
||||
concentricRings.getDimension().getStructures().get(0)
|
||||
.setDistribution(StructureDistribution.CONCENTRIC_RINGS);
|
||||
|
||||
assertFalse(IrisStructureLocator.hasLocatablePlacement(randomSpread, "test:density"));
|
||||
assertFalse(IrisStructureLocator.hasLocatablePlacement(concentricRings, "test:density"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void locatableIndexIncludesAliasesAndSeparatesEditableFromNativePlacements() {
|
||||
IrisData data = mock(IrisData.class);
|
||||
IrisStructure structure = new IrisStructure();
|
||||
structure.setLoadKey("test:city");
|
||||
structure.setVanillaSource("source:city");
|
||||
when(data.load(IrisStructure.class, "test:city_definition", false)).thenReturn(structure);
|
||||
|
||||
IrisStructurePlacement editable = new IrisStructurePlacement();
|
||||
editable.getStructures().add("test:city_definition");
|
||||
IrisStructurePlacement nativePlacement = new IrisStructurePlacement();
|
||||
nativePlacement.getNativeStructures().add(new IrisNativeStructure()
|
||||
.setStructure("source:native_city"));
|
||||
IrisDimension dimension = mock(IrisDimension.class);
|
||||
KList<IrisStructurePlacement> placements = new KList<>();
|
||||
placements.add(editable);
|
||||
placements.add(nativePlacement);
|
||||
Engine engine = mock(Engine.class);
|
||||
when(engine.getData()).thenReturn(data);
|
||||
when(engine.getDimension()).thenReturn(dimension);
|
||||
when(engine.getMinHeight()).thenReturn(-64);
|
||||
when(engine.getHeight()).thenReturn(384);
|
||||
when(dimension.getStructures()).thenReturn(placements);
|
||||
when(dimension.getAllRegions(engine)).thenReturn(new KList<>());
|
||||
when(dimension.getReachableBiomes(engine)).thenReturn(new KList<>());
|
||||
|
||||
assertEquals(Set.of("test:city_definition", "test:city", "source:city", "source:native_city"),
|
||||
IrisStructureLocator.locatableKeys(engine));
|
||||
assertEquals(Set.of("test:city_definition", "test:city", "source:city"),
|
||||
IrisStructureLocator.locatableEditableKeys(engine));
|
||||
assertEquals(Set.of("source:native_city"), IrisStructureLocator.locatableNativeKeys(engine));
|
||||
assertTrue(IrisStructureLocator.hasLocatableEditablePlacement(engine, "SOURCE:CITY"));
|
||||
assertFalse(IrisStructureLocator.hasLocatableEditablePlacement(engine, "source:native_city"));
|
||||
assertTrue(IrisStructureLocator.hasLocatableNativePlacement(engine, "SOURCE:NATIVE_CITY"));
|
||||
assertFalse(IrisStructureLocator.hasLocatableNativePlacement(engine, "source:city"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void suppressesVanillaIsFalseForNullEngine() {
|
||||
assertFalse(IrisStructureLocator.suppressesVanilla(null, "minecraft:ancient_city"));
|
||||
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
package art.arcane.iris.util.common.director.specialhandlers;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.framework.IrisStructureLocator;
|
||||
import art.arcane.iris.engine.framework.StructureReachability;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.engine.object.IrisImportedStructureControl;
|
||||
import art.arcane.iris.engine.object.IrisNativeStructure;
|
||||
import art.arcane.iris.engine.object.IrisNativeStructureDecision;
|
||||
import art.arcane.iris.engine.object.IrisStructure;
|
||||
import art.arcane.iris.engine.object.IrisStructurePlacement;
|
||||
import art.arcane.iris.engine.object.IrisWorld;
|
||||
import art.arcane.iris.engine.object.NativeStructureGenerationStatus;
|
||||
import art.arcane.iris.engine.object.StructureDistribution;
|
||||
import art.arcane.iris.spi.IrisPlatform;
|
||||
import art.arcane.iris.spi.IrisPlatforms;
|
||||
import art.arcane.iris.spi.PlatformStructureHooks;
|
||||
import art.arcane.iris.spi.PlatformWorld;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.director.exceptions.DirectorParsingException;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class StructureHandlerTest {
|
||||
@Test
|
||||
public void registeredEligibilityMatchesFindExecutionTruthTable() {
|
||||
IrisNativeStructureDecision replacement = decision(NativeStructureGenerationStatus.REPLACED_BY_IRIS);
|
||||
IrisNativeStructureDecision generated = decision(NativeStructureGenerationStatus.GENERATE_NATIVE);
|
||||
IrisNativeStructureDecision disabled = decision(NativeStructureGenerationStatus.DISABLED_BY_PACK);
|
||||
|
||||
assertTrue(StructureHandler.isEligibleRegisteredKey(
|
||||
replacement, false, false, true, false, false));
|
||||
assertFalse(StructureHandler.isEligibleRegisteredKey(
|
||||
replacement, false, false, false, true, true));
|
||||
assertTrue(StructureHandler.isEligibleRegisteredKey(
|
||||
replacement, true, true, false, false, true));
|
||||
assertFalse(StructureHandler.isEligibleRegisteredKey(
|
||||
replacement, true, true, false, false, false));
|
||||
assertTrue(StructureHandler.isEligibleRegisteredKey(
|
||||
generated, true, true, false, false, true));
|
||||
assertFalse(StructureHandler.isEligibleRegisteredKey(
|
||||
generated, true, false, false, false, true));
|
||||
assertTrue(StructureHandler.isEligibleRegisteredKey(
|
||||
generated, false, false, false, true, true));
|
||||
assertFalse(StructureHandler.isEligibleRegisteredKey(
|
||||
generated, false, false, false, true, false));
|
||||
assertFalse(StructureHandler.isEligibleRegisteredKey(
|
||||
disabled, true, true, false, true, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requiresActiveIrisEngineBeforeReadingPlatformRegistries() throws DirectorParsingException {
|
||||
IrisPlatforms.unbind();
|
||||
TestStructureHandler handler = new TestStructureHandler(null, true);
|
||||
|
||||
assertTrue(handler.getPossibilities().isEmpty());
|
||||
assertEquals("manual:structure", handler.parse("manual:structure", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filtersAndDeduplicatesSuggestionsForActiveEngine() {
|
||||
IrisData data = mock(IrisData.class);
|
||||
Engine engine = mock(Engine.class);
|
||||
IrisDimension dimension = mock(IrisDimension.class);
|
||||
IrisWorld world = mock(IrisWorld.class);
|
||||
PlatformWorld platformWorld = mock(PlatformWorld.class);
|
||||
IrisPlatform platform = mock(IrisPlatform.class);
|
||||
PlatformStructureHooks hooks = mock(PlatformStructureHooks.class);
|
||||
IrisImportedStructureControl control = new IrisImportedStructureControl();
|
||||
control.getDisabled().add("pack:disabled");
|
||||
control.getDisabled().add("pack:replacement");
|
||||
control.getDisabled().add("pack:dormant_replacement");
|
||||
|
||||
KList<IrisStructurePlacement> placements = new KList<>();
|
||||
placements.add(nativePlacement("pack:replacement", StructureDistribution.RANDOM_SPREAD, 1.0));
|
||||
placements.add(nativePlacement("pack:dormant_replacement", StructureDistribution.DENSITY, 0.0));
|
||||
placements.add(nativePlacement("pack:explicit", StructureDistribution.RANDOM_SPREAD, 1.0));
|
||||
placements.add(nativePlacement("pack:dormant_explicit", StructureDistribution.DENSITY, 0.0));
|
||||
placements.add(nativePlacement("pack:unregistered_native", StructureDistribution.RANDOM_SPREAD, 1.0));
|
||||
placements.add(nativePlacement("iris:custom", StructureDistribution.RANDOM_SPREAD, 1.0));
|
||||
placements.add(editablePlacement("iris:custom_definition"));
|
||||
placements.add(editablePlacement("iris:collision_definition"));
|
||||
|
||||
IrisStructure custom = new IrisStructure();
|
||||
custom.setLoadKey("iris:custom");
|
||||
IrisStructure collision = new IrisStructure();
|
||||
collision.setLoadKey("pack:collision");
|
||||
when(data.load(IrisStructure.class, "iris:custom_definition", false)).thenReturn(custom);
|
||||
when(data.load(IrisStructure.class, "iris:collision_definition", false)).thenReturn(collision);
|
||||
when(engine.getData()).thenReturn(data);
|
||||
when(engine.getDimension()).thenReturn(dimension);
|
||||
when(engine.getWorld()).thenReturn(world);
|
||||
when(engine.getMinHeight()).thenReturn(-64);
|
||||
when(engine.getHeight()).thenReturn(384);
|
||||
when(world.platformWorld()).thenReturn(platformWorld);
|
||||
when(dimension.getImportedStructures()).thenReturn(control);
|
||||
when(dimension.getStructures()).thenReturn(placements);
|
||||
when(dimension.getAllRegions(engine)).thenReturn(new KList<>());
|
||||
when(dimension.getReachableBiomes(engine)).thenReturn(new KList<>());
|
||||
when(platform.structureHooks()).thenReturn(hooks);
|
||||
when(hooks.structureKeys()).thenReturn(List.of(
|
||||
"pack:reachable",
|
||||
"PACK:REACHABLE",
|
||||
"pack:unreachable",
|
||||
"pack:disabled",
|
||||
"pack:replacement",
|
||||
"pack:dormant_replacement",
|
||||
"pack:explicit",
|
||||
"pack:dormant_explicit",
|
||||
"PACK:COLLISION"));
|
||||
when(hooks.reachableStructureKeys(platformWorld)).thenReturn(List.of("PACK:REACHABLE"));
|
||||
|
||||
IrisPlatforms.unbind();
|
||||
IrisPlatforms.bind(platform);
|
||||
try {
|
||||
KList<String> possibilities = new TestStructureHandler(engine, true).getPossibilities();
|
||||
Set<String> distinctPossibilities = new LinkedHashSet<>(possibilities);
|
||||
|
||||
assertEquals(Set.of(
|
||||
"pack:reachable",
|
||||
"pack:replacement",
|
||||
"pack:explicit",
|
||||
"iris:custom_definition",
|
||||
"iris:collision_definition"), distinctPossibilities);
|
||||
assertEquals(distinctPossibilities.size(), possibilities.size());
|
||||
assertFalse(possibilities.stream().anyMatch("pack:collision"::equalsIgnoreCase));
|
||||
assertFalse(possibilities.contains("pack:unregistered_native"));
|
||||
verify(hooks, times(1)).structureKeys();
|
||||
verify(hooks, times(1)).reachableStructureKeys(platformWorld);
|
||||
} finally {
|
||||
IrisStructureLocator.invalidate(engine);
|
||||
StructureReachability.invalidate(engine);
|
||||
IrisPlatforms.unbind();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabledWorldKeepsOnlyUnregisteredEditablePlacements() {
|
||||
IrisData data = mock(IrisData.class);
|
||||
Engine engine = mock(Engine.class);
|
||||
IrisDimension dimension = mock(IrisDimension.class);
|
||||
IrisWorld world = mock(IrisWorld.class);
|
||||
PlatformWorld platformWorld = mock(PlatformWorld.class);
|
||||
IrisPlatform platform = mock(IrisPlatform.class);
|
||||
PlatformStructureHooks hooks = mock(PlatformStructureHooks.class);
|
||||
IrisImportedStructureControl control = new IrisImportedStructureControl();
|
||||
control.getDisabled().add("minecraft:replacement");
|
||||
|
||||
IrisStructure editable = new IrisStructure();
|
||||
editable.setLoadKey("iris:editable");
|
||||
IrisStructure replacement = new IrisStructure();
|
||||
replacement.setLoadKey("iris:replacement");
|
||||
replacement.setVanillaSource("minecraft:replacement");
|
||||
KList<IrisStructurePlacement> placements = new KList<>();
|
||||
placements.add(editablePlacement("iris:editable_definition"));
|
||||
placements.add(editablePlacement("iris:replacement_definition"));
|
||||
placements.add(nativePlacement("minecraft:explicit", StructureDistribution.RANDOM_SPREAD, 1.0));
|
||||
|
||||
when(data.load(IrisStructure.class, "iris:editable_definition", false)).thenReturn(editable);
|
||||
when(data.load(IrisStructure.class, "iris:replacement_definition", false)).thenReturn(replacement);
|
||||
when(engine.getData()).thenReturn(data);
|
||||
when(engine.getDimension()).thenReturn(dimension);
|
||||
when(engine.getWorld()).thenReturn(world);
|
||||
when(engine.getMinHeight()).thenReturn(-64);
|
||||
when(engine.getHeight()).thenReturn(384);
|
||||
when(world.platformWorld()).thenReturn(platformWorld);
|
||||
when(dimension.getImportedStructures()).thenReturn(control);
|
||||
when(dimension.getStructures()).thenReturn(placements);
|
||||
when(dimension.getAllRegions(engine)).thenReturn(new KList<>());
|
||||
when(dimension.getReachableBiomes(engine)).thenReturn(new KList<>());
|
||||
when(platform.structureHooks()).thenReturn(hooks);
|
||||
when(hooks.structureKeys()).thenReturn(List.of(
|
||||
"minecraft:replacement", "minecraft:explicit", "minecraft:reachable"));
|
||||
when(hooks.reachableStructureKeys(platformWorld)).thenReturn(List.of("minecraft:reachable"));
|
||||
|
||||
IrisPlatforms.unbind();
|
||||
IrisPlatforms.bind(platform);
|
||||
try {
|
||||
Set<String> possibilities = new LinkedHashSet<>(
|
||||
new TestStructureHandler(engine, false).getPossibilities());
|
||||
|
||||
assertEquals(Set.of(
|
||||
"minecraft:replacement",
|
||||
"iris:editable_definition",
|
||||
"iris:editable",
|
||||
"iris:replacement_definition",
|
||||
"iris:replacement"), possibilities);
|
||||
assertFalse(possibilities.contains("minecraft:explicit"));
|
||||
assertFalse(possibilities.contains("minecraft:reachable"));
|
||||
} finally {
|
||||
IrisStructureLocator.invalidate(engine);
|
||||
StructureReachability.invalidate(engine);
|
||||
IrisPlatforms.unbind();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propagatesRegistryFailures() {
|
||||
Engine engine = mock(Engine.class);
|
||||
IrisPlatform platform = mock(IrisPlatform.class);
|
||||
PlatformStructureHooks hooks = mock(PlatformStructureHooks.class);
|
||||
IllegalStateException failure = new IllegalStateException("registry unavailable");
|
||||
when(platform.structureHooks()).thenReturn(hooks);
|
||||
when(hooks.structureKeys()).thenThrow(failure);
|
||||
|
||||
IrisPlatforms.unbind();
|
||||
IrisPlatforms.bind(platform);
|
||||
try {
|
||||
IllegalStateException thrown = assertThrows(
|
||||
IllegalStateException.class, () -> new TestStructureHandler(engine, true).getPossibilities());
|
||||
assertSame(failure, thrown);
|
||||
} finally {
|
||||
IrisPlatforms.unbind();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void completionUsesOneReachabilitySnapshotAndLocatableKeys() throws IOException {
|
||||
Path sourcePath = Path.of(
|
||||
"src/main/java/art/arcane/iris/util/common/director/specialhandlers/StructureHandler.java");
|
||||
String source = Files.readString(sourcePath);
|
||||
int methodStart = source.indexOf("public KList<String> getPossibilities()");
|
||||
int methodEnd = source.indexOf("@Override\n public String toString", methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
|
||||
assertEquals(1, occurrences(method, "StructureReachability.reachableKeys(activeEngine)"));
|
||||
assertEquals(0, occurrences(method, "IrisStructureLocator.locatableKeys(activeEngine)"));
|
||||
assertEquals(1, occurrences(method, "IrisStructureLocator.locatableEditableKeys(activeEngine)"));
|
||||
assertTrue(method.indexOf("if (activeEngine == null)")
|
||||
< method.indexOf("IrisPlatforms.get().structureHooks()"));
|
||||
assertFalse(method.contains("INMS"));
|
||||
assertFalse(method.contains("catch ("));
|
||||
}
|
||||
|
||||
private static IrisNativeStructureDecision decision(NativeStructureGenerationStatus status) {
|
||||
return new IrisNativeStructureDecision(status, 0, null, false, false, null, null);
|
||||
}
|
||||
|
||||
private static IrisStructurePlacement nativePlacement(String key, StructureDistribution distribution,
|
||||
double density) {
|
||||
IrisStructurePlacement placement = new IrisStructurePlacement();
|
||||
placement.getNativeStructures().add(new IrisNativeStructure().setStructure(key));
|
||||
placement.setDistribution(distribution);
|
||||
placement.setDensity(density);
|
||||
return placement;
|
||||
}
|
||||
|
||||
private static IrisStructurePlacement editablePlacement(String key) {
|
||||
IrisStructurePlacement placement = new IrisStructurePlacement();
|
||||
placement.getStructures().add(key);
|
||||
return placement;
|
||||
}
|
||||
|
||||
private static int occurrences(String source, String target) {
|
||||
int count = 0;
|
||||
int offset = 0;
|
||||
while ((offset = source.indexOf(target, offset)) >= 0) {
|
||||
count++;
|
||||
offset += target.length();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static final class TestStructureHandler extends StructureHandler {
|
||||
private final Engine activeEngine;
|
||||
private final boolean nativeGenerationEnabled;
|
||||
|
||||
private TestStructureHandler(Engine activeEngine, boolean nativeGenerationEnabled) {
|
||||
this.activeEngine = activeEngine;
|
||||
this.nativeGenerationEnabled = nativeGenerationEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Engine engine() {
|
||||
return activeEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nativeStructureGenerationEnabled() {
|
||||
return nativeGenerationEnabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user