mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-25 13:30:08 +00:00
Merge master into 3D terrain
- Resolve IrisTerrainNormalActuator by keeping master's primitive riverWaterSurfaceStream lookup and primarySurfaceLayerOrNull accessors while retaining the terrain column, span, layer surface and ceiling locals that write air through terrain3D gaps - Resolve HydrologyFootprintCompiler by keeping the MaterializedSurface planned-surface record with terrain ownership beside master's package-private SurfaceRasterIndex, which retains its ownsTerrain override - Resolve HydrologyCaveContainmentPlanner by dropping the nested RecordingCaveVoxelView, SurfaceObservations and ViewObservations types that master promoted to their own files - Port the surface-observation ownership check into CaveSurfaceObservations so cached cave validations replay both height and ownership observations through Observation.matches - Resolve PackExportClosureTest by keeping master's retirement of the source-text packager guard while retaining the snippet export tests, and restore the Files and Path imports those tests use - Resolve IrisBiomeLayerSlopeSourceTest by folding the lower-ledge slope palette case into master's lockLayers parameterization - Recapture the studio schema digest for the terrain3D biome property, its IrisTerrain3D definition, the terrain-3d snippet enumeration and the reworded upperDimensionCarving description, verified by reconstructing the previous digest from the stripped schema
This commit is contained in:
commit
98e51cba8d
520 files changed
+45623
-40734
No files matched your search
+3
-9
@@ -73,15 +73,9 @@ public class IrisModLanguageAssetsTest {
|
||||
|
||||
private Set<String> resourceFiles() throws Exception {
|
||||
URL resource = IrisModLanguageAssetsTest.class.getClassLoader().getResource(ROOT);
|
||||
Path directory;
|
||||
if (resource != null && "file".equals(resource.getProtocol())) {
|
||||
directory = Path.of(resource.toURI());
|
||||
} else {
|
||||
String sources = System.getProperty("iris.moddedCommonSources");
|
||||
assertNotNull("Missing mod language resource directory and source root", sources);
|
||||
directory = Path.of(sources).getParent()
|
||||
.resolve("resources").resolve(ROOT);
|
||||
}
|
||||
assertNotNull("Missing mod language resource directory: " + ROOT, resource);
|
||||
assertEquals("Mod language resources must resolve to a directory on disk", "file", resource.getProtocol());
|
||||
Path directory = Path.of(resource.toURI());
|
||||
try (Stream<Path> paths = Files.list(directory)) {
|
||||
return paths
|
||||
.filter(Files::isRegularFile)
|
||||
|
||||
-21
@@ -9,13 +9,9 @@ import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||
import org.junit.Test;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisModdedChunkGeneratorSpawnTest {
|
||||
@BeforeClass
|
||||
@@ -48,21 +44,4 @@ public class IrisModdedChunkGeneratorSpawnTest {
|
||||
assertEquals(explicitCow, entries.get(2).value());
|
||||
assertEquals(3, entries.get(2).weight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vanillaChunkGenerationMobsUseTheVisibleBiomesVanillaDerivative() throws IOException {
|
||||
Path sourcePath = Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedChunkGenerator.java");
|
||||
String source = Files.readString(sourcePath);
|
||||
int spawnStart = source.indexOf("public void spawnOriginalMobs");
|
||||
int spawnEnd = source.indexOf("@Override", spawnStart + 1);
|
||||
String spawn = source.substring(spawnStart, spawnEnd);
|
||||
|
||||
assertTrue(spawn.contains("spawnTables.initializeVanillaSpawnBiomes(registry)"));
|
||||
assertTrue(spawn.contains("structureBiomeSource.getVisibleSurfaceBiome("));
|
||||
assertTrue(spawn.contains("spawnTables.vanillaSpawnBiome(visibleBiome.value())"));
|
||||
assertTrue(spawn.contains("NaturalSpawner.spawnMobsForChunkGeneration("));
|
||||
assertTrue(spawn.contains("new LegacyRandomSource(RandomSupport.generateUniqueSeed())"));
|
||||
assertTrue(spawn.contains("random.setDecorationSeed(region.getSeed()"));
|
||||
}
|
||||
}
|
||||
+2
-153
@@ -14,9 +14,6 @@ import net.minecraft.server.Bootstrap;
|
||||
import org.junit.BeforeClass;
|
||||
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.Locale;
|
||||
import java.util.Set;
|
||||
@@ -64,16 +61,10 @@ public class IrisModdedStructureParityTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void strongholdRingSearchSamplesOneQuartColumnPerChunk() throws IOException {
|
||||
public void strongholdRingSearchSamplesOneQuartColumnPerChunk() {
|
||||
assertEquals(4, IrisModdedBiomeSource.horizontalBiomeSearchQuartStep(0, 112));
|
||||
assertEquals(1, IrisModdedBiomeSource.horizontalBiomeSearchQuartStep(1, 112));
|
||||
assertEquals(1, IrisModdedBiomeSource.horizontalBiomeSearchQuartStep(0, 111));
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedBiomeSource.java"));
|
||||
assertTrue(source.contains("x, y, z, searchRadius, quartStep, allowed, random, false, sampler"));
|
||||
assertTrue(source.contains(
|
||||
"return super.findBiomeHorizontal(x, y, z, searchRadius, allowed, random, sampler)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -130,126 +121,12 @@ public class IrisModdedStructureParityTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void visibleBiomeResolutionSelectsStackOwnershipBeforeHostCaves() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedBiomeSource.java"));
|
||||
int resolutionStart = source.indexOf("private BiomeResolution resolveBiomeResolution(");
|
||||
int resolutionEnd = source.indexOf("private Holder<Biome> resolveRequiredStructureBiome(", resolutionStart);
|
||||
|
||||
assertTrue(resolutionStart >= 0);
|
||||
assertTrue(resolutionEnd > resolutionStart);
|
||||
String resolution = source.substring(resolutionStart, resolutionEnd);
|
||||
|
||||
assertTrue(resolution.contains("resolveDimensionStackLayer("));
|
||||
assertTrue(resolution.contains("!stackLayer.terrainContext().isSelfReferencing()"));
|
||||
assertTrue(resolution.indexOf("resolveDimensionStackLayer(")
|
||||
< resolution.indexOf("boolean underground"));
|
||||
assertTrue(resolution.contains("includeDimensionStack"));
|
||||
assertTrue(resolution.contains(
|
||||
"return resolveBiomeResolution(engine, quartX, quartY, quartZ, false)"));
|
||||
assertTrue(source.contains("resolution.packName(), resolution.dimension(), customBiome.getId()"));
|
||||
int helperStart = source.indexOf("private static IrisBiome resolveSurfaceStructureBiome(");
|
||||
int helperEnd = source.indexOf("private static Set<String> registeredBiomeKeys(", helperStart);
|
||||
String helper = source.substring(helperStart, helperEnd);
|
||||
assertTrue(helper.contains("engine.getComplex().getTrueBiomeStream().get(blockX, blockZ)"));
|
||||
assertFalse(helper.contains("getDimensionStackContext()"));
|
||||
assertFalse(helper.contains("getSurfaceBiome("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stackBiomesAreVisibleButExcludedFromStructureReachability() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedBiomeSource.java"));
|
||||
|
||||
assertTrue(source.contains("return biomeKeySets().structureRequired()"));
|
||||
assertTrue(source.contains("generatedBiomeKeys.addAll(keys.visibleRequired())"));
|
||||
assertTrue(source.contains("collectStructureBiomeKeys("));
|
||||
assertTrue(source.contains("dimension.getReachableBiomes(() -> data)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void biomeLocatorSearchesTheVisibleStack() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedBiomeSource.java"));
|
||||
int start = source.indexOf("public Pair<BlockPos, Holder<Biome>> findClosestBiome3d(");
|
||||
int end = source.indexOf("private Holder<Biome> getNoiseBiome(", start);
|
||||
String method = source.substring(start, end);
|
||||
|
||||
assertTrue(method.contains("modded_locate_visible_biome"));
|
||||
assertTrue(method.contains("return super.findClosestBiome3d("));
|
||||
assertTrue(method.contains("collectVisibleBiomeHolders()"));
|
||||
assertTrue(method.contains("QuartPos.toBlock(quartX), QuartPos.toBlock(quartZ)"));
|
||||
assertTrue(method.contains("resolveVisibleBiome("));
|
||||
assertFalse(method.contains("Holder<Biome> structureBiome = getNoiseBiome("));
|
||||
assertTrue(method.indexOf("tryAcquireGenerationLease(engine, \"modded_locate_visible_biome\")")
|
||||
< method.indexOf("engine.getDimensionStackContext()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void surfaceSpawnBiomeUsesTheVisibleQuartCell() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedBiomeSource.java"));
|
||||
int start = source.indexOf("Holder<Biome> getVisibleSurfaceBiome(");
|
||||
int end = source.indexOf("boolean isStructureReachable(", start);
|
||||
String method = source.substring(start, end);
|
||||
|
||||
assertTrue(method.contains("QuartPos.fromBlock(blockX)"));
|
||||
assertTrue(method.contains("QuartPos.toBlock(quartX)"));
|
||||
assertTrue(method.contains("QuartPos.fromBlock(internalY + engine.getMinHeight())"));
|
||||
assertTrue(method.contains("stackContext.getLayout(sampleX, sampleZ).surfaceLayer()"));
|
||||
assertTrue(method.contains("visibleBiomeCache"));
|
||||
assertTrue(method.contains("isBiomeCacheable(engine, sampleX, sampleZ)"));
|
||||
assertTrue(method.indexOf("boolean cacheable = isBiomeCacheable(engine, sampleX, sampleZ)")
|
||||
< method.indexOf("Engine.hostHeight(engine, sampleX, sampleZ, true)"));
|
||||
assertCacheGuarded(method, "visibleBiomeCache");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void temporaryNaturalBiomeAnswersAreNotMemoized() throws IOException {
|
||||
public void temporaryNaturalBiomeAnswersAreNotMemoized() {
|
||||
Engine engine = mock(Engine.class);
|
||||
when(engine.answersFromNaturalTerrain(12, -8)).thenReturn(true);
|
||||
assertFalse(IrisModdedBiomeSource.isBiomeCacheable(engine, 12, -8));
|
||||
when(engine.answersFromNaturalTerrain(12, -8)).thenReturn(false);
|
||||
assertTrue(IrisModdedBiomeSource.isBiomeCacheable(engine, 12, -8));
|
||||
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedBiomeSource.java"));
|
||||
int structureStart = source.indexOf("private Holder<Biome> getNoiseBiome(Engine engine");
|
||||
int structureEnd = source.indexOf("Holder<Biome> getVisibleNoiseBiome(", structureStart);
|
||||
String structure = source.substring(structureStart, structureEnd);
|
||||
assertTrue(structure.contains("QuartPos.toBlock(quartX)"));
|
||||
assertTrue(structure.contains("QuartPos.toBlock(quartZ)"));
|
||||
assertCacheGuarded(structure, "structureBiomeCache");
|
||||
|
||||
int visibleStart = source.indexOf(
|
||||
"private Holder<Biome> getVisibleNoiseBiomeWithActiveGenerationLease(");
|
||||
int visibleEnd = source.indexOf("Holder<Biome> getVisibleSurfaceBiome(", visibleStart);
|
||||
String visible = source.substring(visibleStart, visibleEnd);
|
||||
assertTrue(visible.contains("QuartPos.toBlock(quartX)"));
|
||||
assertTrue(visible.contains("QuartPos.toBlock(quartZ)"));
|
||||
assertCacheGuarded(visible, "visibleBiomeCache");
|
||||
|
||||
int predicateStart = source.indexOf("static boolean isBiomeCacheable(");
|
||||
int predicateEnd = source.indexOf("static boolean isMonumentSurfaceBiomeQuery(", predicateStart);
|
||||
String predicate = source.substring(predicateStart, predicateEnd);
|
||||
assertTrue(predicate.contains("return !engine.answersFromNaturalTerrain(blockX, blockZ)"));
|
||||
assertFalse(predicate.contains("getDimensionStackContext()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importedFeaturesCollectOnlyHostOwnedStackCells() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/ModdedImportedFeatureStage.java"));
|
||||
|
||||
assertTrue(source.contains("stackContext.getLayerAt("));
|
||||
assertTrue(source.contains("layer.terrainContext().isSelfReferencing()"));
|
||||
assertTrue(source.contains("section.getBiomes().get(quartX, quartY, quartZ)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -281,17 +158,6 @@ public class IrisModdedStructureParityTest {
|
||||
assertTrue(recursiveKeys.contains("layers:sky/aurora"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void biomeWriterDerivativeFallbackMatchesCanonicalRecursiveKeys() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/ModdedBiomeWriter.java"));
|
||||
|
||||
assertTrue(source.contains(
|
||||
"key.equalsIgnoreCase(dimension.getCustomBiomeKey(custom.getId()))"));
|
||||
assertFalse(source.contains("String dimensionLoadKey = key.substring(0, colon)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structureStateRejectsBiomesOutsideThePackContract() {
|
||||
Set<String> generated = Set.of("minecraft:deep_ocean", "minecraft:dark_forest");
|
||||
@@ -410,21 +276,4 @@ public class IrisModdedStructureParityTest {
|
||||
public void initialEntitySpawnsUseThePaperCompletionMarker() {
|
||||
assertSame(MantleFlag.INITIAL_SPAWNED_MARKER, ModdedWorldManager.INITIAL_SPAWN_COMPLETION_FLAG);
|
||||
}
|
||||
|
||||
private static void assertCacheGuarded(String method, String cacheName) {
|
||||
int predicate = method.indexOf("boolean cacheable = isBiomeCacheable(");
|
||||
int cache = method.indexOf("BiomeHolderTable cache = " + cacheName, predicate);
|
||||
int read = method.indexOf("cache.get(", predicate);
|
||||
int readGuard = method.lastIndexOf("if (cacheable)", read);
|
||||
int write = method.indexOf("cache.put(", read);
|
||||
int writeGuard = method.lastIndexOf("if (cacheable)", write);
|
||||
|
||||
assertTrue(predicate >= 0);
|
||||
assertTrue(cache >= 0);
|
||||
assertTrue(read > predicate);
|
||||
assertTrue(readGuard >= predicate);
|
||||
assertTrue(write > read);
|
||||
assertTrue(writeGuard > read);
|
||||
}
|
||||
|
||||
}
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedDynamicLevelLifecycleContractTest {
|
||||
private static final String SOURCE_ROOT_PROPERTY = "iris.moddedCommonSources";
|
||||
|
||||
@Test
|
||||
public void sharedManagerPublishesDynamicLoadAndUnloadAroundRegistration() throws IOException {
|
||||
String manager = commonSource("ModdedDimensionManager.java");
|
||||
String loader = commonSource("ModdedLoader.java");
|
||||
String injection = method(manager, "private static Handle inject(");
|
||||
String removal = method(manager, "public static boolean remove(");
|
||||
String injectionRollback = method(manager, "private static void rollbackInjection(");
|
||||
String removalRollback = method(manager, "private static void rollbackRemoval(");
|
||||
|
||||
assertTrue(loader.contains("void fireDynamicLevelLoad(MinecraftServer server, ServerLevel level);"));
|
||||
assertTrue(loader.contains("void fireDynamicLevelUnload(MinecraftServer server, ServerLevel level);"));
|
||||
assertBefore(injection, "serverAccess.putLevelIfAbsent(server, key, level);",
|
||||
"fireDynamicLevelLoad(server, level);");
|
||||
assertBefore(removal, "fireDynamicLevelUnload(server, level);",
|
||||
"serverAccess.removeLevel(server, key);");
|
||||
assertTrue(injectionRollback.contains("fireDynamicLevelUnload(server, level);"));
|
||||
assertTrue(removalRollback.contains("fireDynamicLevelLoad(server, level);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void everyLoaderUsesItsNativeLifecycleBus() throws IOException {
|
||||
String fabric = loaderSource("fabric", "art/arcane/iris/fabric/FabricModdedLoader.java");
|
||||
String forge = loaderSource("forge", "art/arcane/iris/forge/ForgeModdedLoader.java");
|
||||
String neoForge = loaderSource("neoforge", "art/arcane/iris/neoforge/NeoForgeModdedLoader.java");
|
||||
|
||||
assertTrue(fabric.contains("ServerLevelEvents.LOAD.invoker().onLevelLoad(server, level);"));
|
||||
assertTrue(fabric.contains("ServerLevelEvents.UNLOAD.invoker().onLevelUnload(server, level);"));
|
||||
assertTrue(forge.contains("LevelEvent.Load.BUS.post(new LevelEvent.Load(level));"));
|
||||
assertTrue(forge.contains("LevelEvent.Unload.BUS.post(new LevelEvent.Unload(level));"));
|
||||
assertTrue(neoForge.contains("NeoForge.EVENT_BUS.post(new LevelEvent.Load(level));"));
|
||||
assertTrue(neoForge.contains("NeoForge.EVENT_BUS.post(new LevelEvent.Unload(level));"));
|
||||
}
|
||||
|
||||
private static String commonSource(String file) throws IOException {
|
||||
return Files.readString(commonRoot().resolve("art/arcane/iris/modded").resolve(file))
|
||||
.replace("\r\n", "\n");
|
||||
}
|
||||
|
||||
private static String loaderSource(String loader, String relative) throws IOException {
|
||||
Path adaptersRoot = commonRoot().getParent().getParent().getParent().getParent();
|
||||
return Files.readString(adaptersRoot.resolve(loader).resolve("src/main/java").resolve(relative))
|
||||
.replace("\r\n", "\n");
|
||||
}
|
||||
|
||||
private static Path commonRoot() {
|
||||
String root = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
if (root == null || root.isBlank()) {
|
||||
throw new IllegalStateException("Missing test property " + SOURCE_ROOT_PROPERTY);
|
||||
}
|
||||
return Path.of(root);
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
if (start < 0) {
|
||||
throw new IllegalArgumentException("Missing source method " + signature);
|
||||
}
|
||||
int open = source.indexOf('{', start);
|
||||
int depth = 0;
|
||||
for (int index = open; index < source.length(); index++) {
|
||||
char current = source.charAt(index);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}' && --depth == 0) {
|
||||
return source.substring(start, index + 1);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source method " + signature);
|
||||
}
|
||||
|
||||
private static void assertBefore(String source, String first, String second) {
|
||||
int firstIndex = source.indexOf(first);
|
||||
int secondIndex = source.indexOf(second);
|
||||
assertTrue("Missing source token " + first, firstIndex >= 0);
|
||||
assertTrue("Missing source token " + second, secondIndex >= 0);
|
||||
assertTrue(first + " must precede " + second, firstIndex < secondIndex);
|
||||
}
|
||||
}
|
||||
-64
@@ -7,12 +7,8 @@ import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
@@ -24,8 +20,6 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ModdedGenerationBootstrapBindingTest {
|
||||
private static final String SOURCE_ROOT_PROPERTY = "iris.moddedCommonSources";
|
||||
|
||||
@Test
|
||||
public void publishedLevelRequiresCurrentGeneratorIdentity() {
|
||||
IrisModdedChunkGenerator generator = mock(IrisModdedChunkGenerator.class, CALLS_REAL_METHODS);
|
||||
@@ -89,62 +83,4 @@ public class ModdedGenerationBootstrapBindingTest {
|
||||
assertSame(dynamicLevel, generator.resolveBoundLevel(server, List.of(dynamicLevel)));
|
||||
verify(server, never()).getLevel(Level.OVERWORLD);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void firstGenerationStagesProvideAuthoritativeLevelContext() throws IOException {
|
||||
String source = source("IrisModdedChunkGenerator.java");
|
||||
String structures = method(source, "public void createStructures(");
|
||||
String references = method(source, "public void createReferences(");
|
||||
assertTrue(structures.contains("Engine current = engine(levelKey);"));
|
||||
assertTrue(references.contains("Engine current = engine(level.getLevel());"));
|
||||
|
||||
String publishedBinding = method(source, "private Engine engine(ResourceKey<Level> levelKey)");
|
||||
assertTrue(publishedBinding.contains("requirePublishedLevel(ModdedEngineBootstrap.currentServer(), levelKey)"));
|
||||
assertTrue(publishedBinding.contains("return bindGenerationLevel(level);"));
|
||||
assertFalse(publishedBinding.contains("ModdedServerLevels.level("));
|
||||
|
||||
String fullBinding = method(source, "private Engine bindGenerationLevel(ServerLevel level)");
|
||||
assertTrue(fullBinding.contains("bindLevel(level);"));
|
||||
assertTrue(fullBinding.contains("Engine bound = readyEngine();"));
|
||||
|
||||
String genericBinding = method(source, "Engine engine()");
|
||||
assertTrue(genericBinding.contains("return bindGenerationLevel(level);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorMixinIsNotPartOfGenerationBootstrap() throws IOException {
|
||||
String sourceRoot = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
assertTrue(sourceRoot != null && !sourceRoot.isBlank());
|
||||
Path root = Path.of(sourceRoot);
|
||||
assertFalse(Files.exists(root.resolve(Path.of("art", "arcane", "iris", "modded", "mixin",
|
||||
"ServerLevelBindingMixin.java"))));
|
||||
String mixinConfig = Files.readString(root.getParent().resolve("resources/irisworldgen.entity.mixins.json"));
|
||||
assertFalse(mixinConfig.contains("ServerLevelBindingMixin"));
|
||||
}
|
||||
|
||||
private String source(String fileName) throws IOException {
|
||||
String sourceRoot = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
assertTrue(sourceRoot != null && !sourceRoot.isBlank());
|
||||
return Files.readString(Path.of(sourceRoot, "art", "arcane", "iris", "modded", fileName));
|
||||
}
|
||||
|
||||
private String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
assertTrue(start >= 0);
|
||||
int body = source.indexOf('{', start);
|
||||
assertTrue(body >= 0);
|
||||
int depth = 0;
|
||||
for (int index = body; index < source.length(); index++) {
|
||||
char token = source.charAt(index);
|
||||
if (token == '{') {
|
||||
depth++;
|
||||
} else if (token == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return source.substring(start, index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source contract method: " + signature);
|
||||
}
|
||||
}
|
||||
-203
@@ -1,203 +0,0 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedGenerationHistoryRoutingContractTest {
|
||||
private static final String SOURCE_ROOT_PROPERTY = "iris.moddedCommonSources";
|
||||
|
||||
@Test
|
||||
public void startupPublishesOnlyAnImmutableHistoryBoundRuntime() throws IOException {
|
||||
String source = source("ModdedWorldEngines.java");
|
||||
String create = method(source, "private static Engine create(");
|
||||
String createHistoryEngine = method(source, "private static Engine createHistoryEngine(");
|
||||
String buildEngine = method(source, "private static IrisEngine buildEngine(");
|
||||
|
||||
assertTrue(create.contains("ModdedGenerationHistoryStorage.openOrAdopt("));
|
||||
assertFalse(create.contains("resolvePack("));
|
||||
assertTrue(createHistoryEngine.contains("candidate.attachGenerationHistory("));
|
||||
assertTrue(createHistoryEngine.contains("IrisBoundarySignatureSampler.INSTANCE"));
|
||||
assertTrue(createHistoryEngine.contains("getGenerationTransitionWidthBlocks()"));
|
||||
assertTrue(createHistoryEngine.contains("router.preloadActiveRuntimes();"));
|
||||
assertBefore(createHistoryEngine, "history.prepareCurrentGenerator(",
|
||||
"opened = ModdedGenerationHistoryStorage.resolveActive(history)");
|
||||
assertBefore(createHistoryEngine, "opened = ModdedGenerationHistoryStorage.resolveActive(history)",
|
||||
"IrisEngine candidate = buildEngine(level, seed, opened)");
|
||||
String promoted = method(createHistoryEngine,
|
||||
"if (history.activeActivation().activationId() != openedActivationId)");
|
||||
assertBefore(promoted, "close(candidate);", "ModdedGenerationHistoryStorage.resolveActive(history)");
|
||||
assertBefore(promoted, "ModdedGenerationHistoryStorage.resolveActive(history)",
|
||||
"candidate = buildEngine(level, seed, promoted)");
|
||||
assertTrue(createHistoryEngine.contains("GenerationHistoryRuntimeRouter.attach("));
|
||||
assertTrue(buildEngine.contains("active.packRoot().toFile()"));
|
||||
assertTrue(buildEngine.contains("history.paths().activationMantleRoot(activation.activationId())"));
|
||||
assertTrue(buildEngine.contains("epoch.kernelVersion()"));
|
||||
assertTrue(buildEngine.contains("transitionPlan"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onlyExplicitTransientStudioEnginesBypassHistory() throws IOException {
|
||||
String manager = source("ModdedDimensionManager.java");
|
||||
String transientCreate = method(manager, "public static Handle createTransientStudio(");
|
||||
String persistentCreate = method(manager, "public static Handle createPersistent(");
|
||||
String engines = source("ModdedWorldEngines.java");
|
||||
String studio = method(engines, "private static IrisEngine createTransientStudioEngine(");
|
||||
String generator = source("IrisModdedChunkGenerator.java");
|
||||
String bypass = method(generator, "boolean allowsGenerationHistoryBypass(");
|
||||
String route = method(generator, "private GenerationHistoryRuntimeRouter.RuntimeRoute openHistoryRoute(");
|
||||
|
||||
assertTrue(transientCreate.contains("ModdedGenerationMode.TRANSIENT_STUDIO"));
|
||||
assertTrue(persistentCreate.contains("ModdedGenerationMode.PERSISTENT_CREATE"));
|
||||
assertTrue(studio.contains("IrisData.openRuntime(packDir)"));
|
||||
assertTrue(studio.contains("IrisEngine.InitializationMode.STUDIO"));
|
||||
assertFalse(studio.contains("ModdedGenerationHistoryStorage"));
|
||||
assertFalse(studio.contains("attachGenerationHistory("));
|
||||
assertTrue(bypass.contains("generationMode != ModdedGenerationMode.TRANSIENT_STUDIO"));
|
||||
assertTrue(bypass.contains("irisEngine.isStudio()"));
|
||||
assertTrue(bypass.contains("Only an explicitly transient Iris Studio engine"));
|
||||
assertBefore(route, "allowsGenerationHistoryBypass(current)", "requireHistoryRouter(current, operation)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void everyCoordinateBearingChunkStageOpensItsRoutedRuntime() throws IOException {
|
||||
String source = source("IrisModdedChunkGenerator.java");
|
||||
|
||||
assertRuntimeRoute(source, "public Pair<BlockPos, Holder<Structure>> findNearestMapStructure(");
|
||||
assertCoordinateRoute(source, "public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(");
|
||||
assertRuntimeRoute(source, "public CompletableFuture<ChunkAccess> createBiomes(");
|
||||
assertRuntimeRoute(source, "public void applyCarvers(");
|
||||
assertRuntimeRoute(source, "public void buildSurface(");
|
||||
assertRuntimeRoute(source, "public void applyBiomeDecoration(");
|
||||
assertRuntimeRoute(source, "public void createStructures(");
|
||||
assertRuntimeRoute(source, "public void createReferences(");
|
||||
assertRuntimeRoute(source, "public void spawnOriginalMobs(");
|
||||
assertCoordinateRoute(source, "public int getBaseHeight(");
|
||||
assertCoordinateRoute(source, "public NoiseColumn getBaseColumn(");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asyncTerrainKeepsTheRouteUntilDurableSemanticClaim() throws IOException {
|
||||
String source = source("IrisModdedChunkGenerator.java");
|
||||
String fill = method(source, "public CompletableFuture<ChunkAccess> fillFromNoise(");
|
||||
String terrain = method(source, "private ChunkAccess generateTerrain(");
|
||||
String completion = method(source, "private static CompletableFuture<ChunkAccess> closeRouteOnCompletion(");
|
||||
|
||||
assertBefore(fill, "openHistoryRoute(", "CompletableFuture.supplyAsync(");
|
||||
assertTrue(fill.contains("generateTerrain(chunk, generationEngine, pos, air, route)"));
|
||||
assertTrue(fill.contains("return closeRouteOnCompletion(pipeline, route);"));
|
||||
assertTrue(terrain.contains("openHistoryRuntimeScope(route)"));
|
||||
assertBefore(terrain, "generationEngine.generate(", "writeBlocks(chunk, blocks, dimMinY, height)");
|
||||
assertBefore(terrain, "writeBlocks(chunk, blocks, dimMinY, height)", "route.claimGeneratedSemantics(");
|
||||
assertTrue(terrain.contains("chunk.getBlockState(position.set(x, dimMinY + y, z))"));
|
||||
assertTrue(terrain.contains("state.isAir() || state.liquid()"));
|
||||
assertBefore(terrain, "route.claimGeneratedSemantics(", "ModdedNativeTerrainReceipts.persist(chunk, route)");
|
||||
assertBefore(terrain, "ModdedNativeTerrainReceipts.persist(chunk, route)", "return chunk;");
|
||||
assertTrue(completion.contains("pipeline.whenComplete("));
|
||||
assertTrue(completion.contains("route.close();"));
|
||||
assertBefore(completion, "pipeline.whenComplete(", "route.detachThread()");
|
||||
assertBefore(completion, "route.detachThread()", "return completion;");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void biomeQueriesRouteEachCoordinateAndKeepRuntimeSpecificCaches() throws IOException {
|
||||
String source = source("IrisModdedBiomeSource.java");
|
||||
String structure = method(source, "public Holder<Biome> getNoiseBiome(");
|
||||
String visible = method(source, "Holder<Biome> getVisibleNoiseBiome(");
|
||||
String batch = method(source, "public Set<Holder<Biome>> getBiomesWithin(");
|
||||
String resolution = method(source, "private Holder<Biome> resolveVisibleBiome(");
|
||||
|
||||
assertTrue(structure.contains("openHistoryCoordinateScope("));
|
||||
assertTrue(visible.contains("openHistoryCoordinateScope("));
|
||||
assertTrue(batch.contains("quartX << 2, quartZ << 2"));
|
||||
assertTrue(source.contains("int runtimeIdentity = engine.getCacheID();"));
|
||||
assertTrue(source.contains("cache.get(runtimeIdentity, key)"));
|
||||
assertTrue(source.contains("cache.put(runtimeIdentity, key, resolved)"));
|
||||
assertBefore(resolution, "historicalPhysicalBiomeKeyAt(", "ModdedWorldgenIds.biomeRef(engine");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pendingNativeStagesPreserveSavedStartsAndGuardNewFeatureFootprints() throws IOException {
|
||||
String generator = source("IrisModdedChunkGenerator.java");
|
||||
String discrete = method(generator, "private boolean allowsRoutedDiscreteGeneration(");
|
||||
String decoration = method(generator, "public void applyBiomeDecoration(");
|
||||
String structures = method(generator, "public void createStructures(");
|
||||
String mobs = method(generator, "public void spawnOriginalMobs(");
|
||||
String nativeStage = source("ModdedNativeStructureStage.java");
|
||||
String adjust = method(nativeStage, "void adjustGeneratedStructures(");
|
||||
String place = method(nativeStage, "void placeVanillaStructures(");
|
||||
String savedStart = method(nativeStage, "private boolean isHistoricalStructureStart(");
|
||||
|
||||
assertTrue(discrete.contains("chunk.getPersistedStatus().isOrAfter(stage)"));
|
||||
assertFalse(discrete.contains("router.history().activeActivation().activationId()"));
|
||||
assertFalse(discrete.contains("return true;"));
|
||||
assertTrue(discrete.contains("allowsNewGenerationChunk("));
|
||||
assertTrue(discrete.contains("NativeGenerationWriteGuard.allowsPendingStage(current, chunk, stage)"));
|
||||
assertTrue(decoration.contains("ChunkStatus.FEATURES"));
|
||||
assertTrue(structures.contains("ChunkStatus.STRUCTURE_STARTS"));
|
||||
assertTrue(mobs.contains("ChunkStatus.SPAWN"));
|
||||
assertBefore(decoration, "chunk.getPersistedStatus().isOrAfter(ChunkStatus.FEATURES)",
|
||||
"nativeStructures.placeVanillaStructures(");
|
||||
assertBefore(decoration, "nativeStructures.placeVanillaStructures(", "allowsRoutedDiscreteGeneration(");
|
||||
assertBefore(decoration, "allowsRoutedDiscreteGeneration(", "importedFeatures.run(");
|
||||
assertBefore(decoration, "NativeGenerationWriteGuard.allowsDecoration(", "importedFeatures.run(");
|
||||
assertBefore(structures, "allowsRoutedDiscreteGeneration(", "super.createStructures(");
|
||||
assertBefore(mobs, "allowsRoutedDiscreteGeneration(", "NaturalSpawner.spawnMobsForChunkGeneration(");
|
||||
assertBefore(adjust, "allowsNewGenerationFootprint(", "recordWorldCheckStructureShift(");
|
||||
assertBefore(place, "!isHistoricalStructureStart(current, world, start)", "allowsNewGenerationFootprint(");
|
||||
assertTrue(savedStart.contains("world.getChunk(origin.x(), origin.z(), ChunkStatus.EMPTY, false)"));
|
||||
assertTrue(savedStart.contains("ModdedNativeTerrainReceipts.structureActivation(source)"));
|
||||
assertBefore(structures, "super.createStructures(", "ModdedNativeTerrainReceipts.persistStructureActivation(");
|
||||
}
|
||||
|
||||
private static void assertRuntimeRoute(String source, String signature) {
|
||||
String body = method(source, signature);
|
||||
assertTrue(signature + " must open a generation-history route", body.contains("openHistoryRoute("));
|
||||
assertTrue(signature + " must open its routed runtime", body.contains("openHistoryRuntimeScope(route)"));
|
||||
}
|
||||
|
||||
private static void assertCoordinateRoute(String source, String signature) {
|
||||
String body = method(source, signature);
|
||||
assertTrue(signature + " must route its coordinate", body.contains("openHistoryCoordinateScope("));
|
||||
}
|
||||
|
||||
private static void assertBefore(String source, String first, String second) {
|
||||
int firstIndex = source.indexOf(first);
|
||||
int secondIndex = source.indexOf(second);
|
||||
assertTrue("Missing source contract token: " + first, firstIndex >= 0);
|
||||
assertTrue("Missing source contract token: " + second, secondIndex >= 0);
|
||||
assertTrue(first + " must occur before " + second, firstIndex < secondIndex);
|
||||
}
|
||||
|
||||
private static String source(String fileName) throws IOException {
|
||||
String sourceRoot = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
assertTrue("Missing system property " + SOURCE_ROOT_PROPERTY,
|
||||
sourceRoot != null && !sourceRoot.isBlank());
|
||||
return Files.readString(Path.of(sourceRoot, "art", "arcane", "iris", "modded", fileName));
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
assertTrue("Missing source contract signature: " + signature, start >= 0);
|
||||
int openBrace = source.indexOf('{', start);
|
||||
assertTrue("Missing source contract method body: " + signature, openBrace >= 0);
|
||||
int depth = 0;
|
||||
for (int index = openBrace; index < source.length(); index++) {
|
||||
char current = source.charAt(index);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return source.substring(start, index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source contract method: " + signature);
|
||||
}
|
||||
}
|
||||
-244
@@ -1,244 +0,0 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedGenerationLeaseContractTest {
|
||||
private static final String SOURCE_ROOT_PROPERTY = "iris.moddedCommonSources";
|
||||
|
||||
@Test
|
||||
public void biomeRuntimeReadsAreGenerationLeased() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/IrisModdedBiomeSource.java");
|
||||
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(engine, \"modded_structure_biome\")"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(engine, \"modded_visible_biome\")"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(engine, \"modded_biomes_within\")"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(engine, \"modded_structure_reachability\")"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(engine, \"modded_structure_state_biome\")"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(engine, \"modded_structure_biome_keys\")"));
|
||||
assertTrue(source.contains("engine == null || engine.isClosed()"));
|
||||
assertTrue(source.contains("catch (GenerationSessionException e)"));
|
||||
assertTrue(source.contains("e.isExpectedTeardown()"));
|
||||
assertTrue(source.contains("Iris structure biome key lookup was rejected during an engine transition"));
|
||||
assertTrue(source.contains("int runtimeIdentity = engine.getCacheID();"));
|
||||
assertTrue(source.contains("cache.get(runtimeIdentity, key)"));
|
||||
assertTrue(source.contains("cache.put(runtimeIdentity, key, resolved)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void terrainAndHeightQueriesShareGenerationSessions() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/IrisModdedChunkGenerator.java");
|
||||
|
||||
assertTrue(source.contains("generationEngine.acquireGenerationLease(\"modded_chunk_pipeline\")"));
|
||||
assertTrue(source.contains("current.acquireGenerationLease(\"modded_base_height\")"));
|
||||
assertTrue(source.contains("current.acquireGenerationLease(\"modded_base_column\")"));
|
||||
assertTrue(source.contains("current.acquireGenerationLease(\"modded_configured_biome_keys\")"));
|
||||
assertTrue(source.contains("generationEngine.isClosing() || e.isExpectedTeardown()"));
|
||||
String terrain = method(source, "private ChunkAccess generateTerrain(");
|
||||
int sessionFailure = terrain.indexOf("catch (GenerationSessionException e)");
|
||||
int generalFailure = terrain.indexOf("catch (Throwable e)", sessionFailure);
|
||||
String sessionHandler = terrain.substring(sessionFailure, generalFailure);
|
||||
assertTrue(sessionHandler.contains("throw new IllegalStateException"));
|
||||
assertFalse(sessionHandler.contains("return chunk;"));
|
||||
|
||||
String references = method(source, "public void createReferences(");
|
||||
assertTrue(references.contains("requireGenerationLease(current, \"modded_create_references\")"));
|
||||
assertTrue(references.contains("IrisContext.open(current, lease.sessionId(), null)"));
|
||||
assertTrue(references.contains("NativeStructureReferenceRepair.createReferences("));
|
||||
assertFalse(references.contains("super.createReferences(level, structureManager, chunk);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preservationDereferencesSharedPackData() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/service/ModdedPreservationService.java");
|
||||
int dereference = source.indexOf("public void dereference()");
|
||||
int irisData = source.indexOf("IrisData.dereference();", dereference);
|
||||
int trackedResources = source.indexOf("threads.removeIf", dereference);
|
||||
|
||||
assertTrue(dereference >= 0);
|
||||
assertTrue(irisData > dereference);
|
||||
assertTrue(trackedResources > irisData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hotloadShutdownOnlyTargetsItsOwnWorld() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/service/ModdedStudioHotloadService.java");
|
||||
int shutdown = source.indexOf("public void shutdownPregenerator(Engine engine)");
|
||||
int nextMethod = source.indexOf("private boolean throttled", shutdown);
|
||||
String method = source.substring(shutdown, nextMethod);
|
||||
|
||||
assertTrue(method.contains("ModdedPregenJob.shutdownForWorld(world.identity());"));
|
||||
assertFalse(method.contains("PregeneratorJob.shutdownInstance();"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maintenanceOwnsOneGenerationSessionUntilWorkCompletes() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/service/ModdedEngineMaintenanceService.java");
|
||||
String maintain = method(source, "private void maintain(Engine engine, long scheduledAt)");
|
||||
|
||||
assertTrue(maintain.contains("engine.acquireGenerationLease(\"modded_engine_maintenance\")"));
|
||||
assertTrue(maintain.contains("IrisContext.open(engine, lease.sessionId(), null)"));
|
||||
assertTrue(maintain.contains("exception.isExpectedTeardown()"));
|
||||
assertTrue(source.contains("active.awaitTermination(INTERRUPT_DRAIN_TIMEOUT_SECONDS, TimeUnit.SECONDS)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void packDownloadAdmissionPrecedesAsyncDispatch() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/command/IrisModdedCommands.java");
|
||||
String download = method(source, "static int download(CommandSourceStack source, String rawRequest)");
|
||||
int admission = download.indexOf("LifecycleOperationCoordinator.get().acquire(");
|
||||
int executionTracking = download.indexOf("new PackDownloadExecution(");
|
||||
int dispatch = download.indexOf("scheduler.asyncIfRunning(execution, execution::cancel)");
|
||||
|
||||
assertTrue(admission >= 0);
|
||||
assertTrue(executionTracking > admission);
|
||||
assertTrue(dispatch > admission);
|
||||
assertTrue(download.contains("execution.cancel();"));
|
||||
|
||||
String execution = method(source, "private static void executeDownload(");
|
||||
assertTrue(execution.contains("PackDownloader.DownloadCancellation cancellation"));
|
||||
assertTrue(execution.contains("catch (PackDownloader.PackDownloadCancelledException error)"));
|
||||
assertTrue(execution.contains("dispatchDownloadFeedback(source,"));
|
||||
assertFalse(execution.contains("scheduler.global("));
|
||||
assertFalse(execution.contains("lease.close();"));
|
||||
|
||||
String feedback = method(source, "private static void dispatchDownloadFeedback(");
|
||||
assertTrue(feedback.contains("source.getServer().execute(feedback);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void packDownloadsDrainBeforeModdedSchedulerShutdown() throws IOException {
|
||||
String commands = source("art/arcane/iris/modded/command/IrisModdedCommands.java");
|
||||
String shutdownDownloads = method(commands, "public static void shutdownDownloads()");
|
||||
assertTrue(shutdownDownloads.contains("downloadAdmissionOpen = false;"));
|
||||
assertTrue(shutdownDownloads.contains("execution.cancel();"));
|
||||
assertTrue(shutdownDownloads.contains("execution.await("));
|
||||
|
||||
String bootstrap = source("art/arcane/iris/modded/ModdedEngineBootstrap.java");
|
||||
String stop = method(bootstrap, "public static void stop()");
|
||||
int downloads = stop.indexOf("IrisModdedCommands::shutdownDownloads");
|
||||
int scheduler = stop.indexOf("scheduler::shutdown");
|
||||
assertTrue(downloads >= 0);
|
||||
assertTrue(scheduler > downloads);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moddedSchedulerRejectsAndCancelsAbandonedDownloadSubmissions() throws IOException {
|
||||
String scheduler = source("art/arcane/iris/modded/ModdedScheduler.java");
|
||||
String dispatch = method(scheduler, "public boolean asyncIfRunning(Runnable task, Runnable rejection)");
|
||||
assertTrue(dispatch.contains("RejectionAwareTask"));
|
||||
assertTrue(dispatch.contains("Objects.requireNonNull(rejection"));
|
||||
|
||||
String shutdown = method(scheduler, "public void shutdown()");
|
||||
assertTrue(shutdown.contains("asyncExecutor.shutdownNow()"));
|
||||
assertTrue(shutdown.contains("rejectAbandonedTasks(abandonedTasks);"));
|
||||
|
||||
String reset = method(scheduler, "public void reset()");
|
||||
assertTrue(reset.contains("asyncExecutor.getQueue().drainTo(abandonedTasks);"));
|
||||
assertTrue(reset.contains("rejectAbandonedTasks(abandonedTasks);"));
|
||||
|
||||
String rejection = method(scheduler, "private static void rejectAbandonedTasks(");
|
||||
assertTrue(rejection.contains("rejectionAwareTask.reject();"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blockingPregenShutdownDefersItsFinalSaveToTheServerThread() throws IOException {
|
||||
String jobSource = source("art/arcane/iris/modded/command/ModdedPregenJob.java");
|
||||
String shutdown = method(jobSource, "private static boolean shutdownAndSave(");
|
||||
assertTrue(shutdown.contains("deferFinalSaveToServerThread()"));
|
||||
assertTrue(shutdown.contains("completeDeferredFinalSave()"));
|
||||
assertTrue(shutdown.contains("cancelDeferredFinalSave()"));
|
||||
|
||||
String methodSource = source("art/arcane/iris/modded/command/ModdedPregenMethod.java");
|
||||
String close = method(methodSource, "public void close()");
|
||||
assertTrue(close.contains("deferFinalSaveIfRequested()"));
|
||||
|
||||
String await = method(methodSource, "private void awaitFinalSave(");
|
||||
assertTrue(await.contains("FINAL_SAVE_POLL_MILLIS"));
|
||||
assertTrue(await.contains("deferFinalSaveIfRequested()"));
|
||||
|
||||
String complete = method(methodSource, "void completeDeferredFinalSave()");
|
||||
int cancel = complete.indexOf("cancelQueuedFinalSave();");
|
||||
int directSave = complete.indexOf("saveLevelOnServerThread();");
|
||||
assertTrue(cancel >= 0);
|
||||
assertTrue(directSave > cancel);
|
||||
|
||||
String queuedSave = method(methodSource, "private void executeFinalSave(");
|
||||
assertTrue(queuedSave.contains("if (!request.claim())"));
|
||||
String cancelRequest = method(methodSource, "private void cancelQueuedFinalSave()");
|
||||
assertTrue(cancelRequest.contains("request.cancel();"));
|
||||
String pending = method(methodSource, "boolean hasPendingFinalSave()");
|
||||
assertTrue(pending.contains("queuedFinalSave.get() != null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void firstPregenChunkFailureRetainsItsFullCause() throws IOException {
|
||||
String methodSource = source("art/arcane/iris/modded/command/ModdedPregenMethod.java");
|
||||
String logFailure = method(methodSource, "private void logChunkFailure(");
|
||||
|
||||
assertTrue(methodSource.contains("AtomicBoolean failureDetailLogged"));
|
||||
assertTrue(logFailure.contains("unwrap(failure)"));
|
||||
assertTrue(logFailure.contains("failureDetailLogged.compareAndSet(false, true)"));
|
||||
assertTrue(logFailure.contains("x, z, cause);"));
|
||||
assertTrue(logFailure.contains("cause.toString()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pregenReportsOnlyCleanupCallbacksFromCoveredTargets() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/command/ModdedPregenMethod.java");
|
||||
String cleanup = method(source, "private void cleanupMantleChunksCoveredBy(");
|
||||
|
||||
assertTrue(source.contains("cleanupMantleChunksCoveredBy(x, z, listener);"));
|
||||
assertFalse(source.contains("cleanupMantleChunk(x, z);"));
|
||||
assertTrue(cleanup.contains("cleanupChunksCoveredBy(x, z, true, listener::onChunkCleaned)"));
|
||||
assertFalse(cleanup.contains("forceCleanupChunk"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidPregenBoundsFailBeforeRuntimeMutation() throws IOException {
|
||||
String jobSource = source("art/arcane/iris/modded/command/ModdedPregenJob.java");
|
||||
String start = method(jobSource, "public static boolean start(");
|
||||
int taskConstruction = start.indexOf("PregenTask task = PregenTask.builder()");
|
||||
int profileMutation = start.indexOf("PregenPerformanceProfile.apply(engine)");
|
||||
|
||||
assertTrue(taskConstruction >= 0);
|
||||
assertTrue(profileMutation > taskConstruction);
|
||||
|
||||
String commandSource = source("art/arcane/iris/modded/command/ModdedPregenCommands.java");
|
||||
String command = method(commandSource, "static int pregenStart(");
|
||||
assertTrue(command.contains("catch (IllegalArgumentException failure)"));
|
||||
assertTrue(command.contains("IrisModdedCommands.fail(source, failure.getMessage())"));
|
||||
}
|
||||
|
||||
private static String source(String relativePath) throws IOException {
|
||||
String root = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
assertTrue("Missing system property " + SOURCE_ROOT_PROPERTY, root != null && !root.isBlank());
|
||||
return Files.readString(Path.of(root, relativePath));
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
assertTrue("Missing source contract signature: " + signature, start >= 0);
|
||||
int openBrace = source.indexOf('{', start);
|
||||
assertTrue("Missing source contract method body: " + signature, openBrace >= 0);
|
||||
int depth = 0;
|
||||
for (int index = openBrace; index < source.length(); index++) {
|
||||
char current = source.charAt(index);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return source.substring(start, index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source contract method: " + signature);
|
||||
}
|
||||
}
|
||||
-58
@@ -2,32 +2,9 @@ package art.arcane.iris.modded;
|
||||
|
||||
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.stream.Stream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* The level router is a switch statement, which the compiler does not check for exhaustiveness. A level added
|
||||
* to {@link art.arcane.iris.spi.LogLevel} with no arm here would compile and then drop every message at that
|
||||
* level without a trace, so the fallback arm is what keeps a new level visible on the modded loaders.
|
||||
*/
|
||||
public class ModdedIrisLogLevelCoverageTest {
|
||||
@Test
|
||||
public void everyLogLevelReachesTheLoggerIncludingOnesAddedLater() throws IOException {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/ModdedIrisLog.java"));
|
||||
int router = source.indexOf("public static void log(LogLevel level, String message)");
|
||||
assertTrue("log(LogLevel, String) not found", router >= 0);
|
||||
|
||||
String body = source.substring(router, source.indexOf("public static void debug(", router));
|
||||
assertTrue(body, body.contains("default -> info(message);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void slf4jStyleArgumentsAndTrailingThrowableArePreserved() {
|
||||
RuntimeException failure = new RuntimeException("broken");
|
||||
@@ -37,39 +14,4 @@ public class ModdedIrisLogLevelCoverageTest {
|
||||
assertEquals("chunk 4,9 failed", rendered.message());
|
||||
assertEquals(failure, rendered.error());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formattedDebugThrowableUsesTheVisibleDebugRouteWhenEnabled() throws IOException {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/ModdedIrisLog.java"));
|
||||
int start = source.indexOf("public static void debug(String format, Object... arguments)");
|
||||
int end = source.indexOf("public static void info(String message)", start);
|
||||
assertTrue("formatted debug overload not found", start >= 0);
|
||||
assertTrue("formatted debug overload boundary not found", end > start);
|
||||
String body = source.substring(start, end);
|
||||
|
||||
assertTrue(body, body.contains("if (!debugEnabled())"));
|
||||
assertTrue(body, body.contains("LOGGER.info(\"[Iris/DEBUG] \" + clean(rendered.message()), rendered.error())"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moddedProductionUsesTheIrisLogFrontDoor() throws IOException {
|
||||
Path root = Path.of(System.getProperty("iris.moddedCommonSources"));
|
||||
try (Stream<Path> files = Files.walk(root)) {
|
||||
List<Path> bypasses = files
|
||||
.filter(path -> path.toString().endsWith(".java"))
|
||||
.filter(path -> !path.getFileName().toString().equals("ModdedIrisLog.java"))
|
||||
.filter(path -> {
|
||||
try {
|
||||
String source = Files.readString(path);
|
||||
return source.contains("LoggerFactory.getLogger")
|
||||
|| source.contains("private static final Logger LOGGER");
|
||||
} catch (IOException unreadable) {
|
||||
throw new IllegalStateException(unreadable);
|
||||
}
|
||||
})
|
||||
.toList();
|
||||
assertTrue(bypasses.toString(), bypasses.isEmpty());
|
||||
}
|
||||
}
|
||||
}
|
||||
-23
@@ -2,10 +2,6 @@ package art.arcane.iris.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
@@ -48,25 +44,6 @@ public class ModdedLevelDataAttachmentTest {
|
||||
assertNull(failure.getCause());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializesLevelDataBeforeBindingAndPublication() throws IOException {
|
||||
String source = Files.readString(sourcePath("ModdedDimensionManager.java"));
|
||||
int construction = source.indexOf("ServerLevel level = new ServerLevel(");
|
||||
int initialization = source.indexOf("serverAccess.initializeLevelData(server, level);", construction);
|
||||
int binding = source.indexOf("generator.bindLevel(level);", initialization);
|
||||
int publication = source.indexOf("serverAccess.putLevelIfAbsent(server, key, level);", binding);
|
||||
|
||||
assertTrue(construction >= 0);
|
||||
assertTrue(initialization > construction);
|
||||
assertTrue(binding > initialization);
|
||||
assertTrue(publication > binding);
|
||||
}
|
||||
|
||||
private static Path sourcePath(String fileName) {
|
||||
return Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded", fileName);
|
||||
}
|
||||
|
||||
public static final class AttachedLevelData {
|
||||
private RootData rootData;
|
||||
private DimensionKey dimensionKey;
|
||||
|
||||
-436
@@ -1,436 +0,0 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedLifecycleFailureContractTest {
|
||||
private static final String SOURCE_ROOT_PROPERTY = "iris.moddedCommonSources";
|
||||
|
||||
@Test
|
||||
public void existingDimensionStagesWithoutReplacingTheLiveEngine() throws IOException {
|
||||
String managerSource = source("ModdedDimensionManager.java");
|
||||
String create = method(managerSource, "private static Handle create(");
|
||||
int existingStart = requiredIndex(create, "if (existing != null");
|
||||
int presentStart = requiredIndex(create, "if (serverAccess.hasLevel(server, key))", existingStart + 1);
|
||||
int injectionStart = requiredIndex(create, "try {", presentStart);
|
||||
|
||||
assertFalse(create.substring(existingStart, presentStart).contains("repointAndBind("));
|
||||
assertFalse(create.substring(presentStart, injectionStart).contains("repointAndBind("));
|
||||
assertBefore(create, "ModdedGenerationHistoryStorage.createOrStage(", "if (existing != null");
|
||||
|
||||
String generatorSource = source("IrisModdedChunkGenerator.java");
|
||||
String repointAndBind = method(generatorSource, "void repointAndBind(");
|
||||
assertBefore(repointAndBind, "ModdedWorldEngines.prepareReplacement(",
|
||||
"ModdedWorldEngines.installReplacement(");
|
||||
assertBefore(repointAndBind, "ModdedWorldEngines.installReplacement(", "this.activePack = pack;");
|
||||
String failure = catchBlock(repointAndBind);
|
||||
assertTrue(failure.contains("ModdedWorldEngines.closeUnregistered(replacement);"));
|
||||
assertTrue(failure.contains("addSuppressed("));
|
||||
|
||||
String worldEnginesSource = source("ModdedWorldEngines.java");
|
||||
String installReplacement = method(worldEnginesSource, "static void installReplacement(");
|
||||
assertTrue(installReplacement.contains("ENGINES.compute("));
|
||||
assertBefore(installReplacement, "close(current);", "return activeReplacement;");
|
||||
assertFalse(installReplacement.contains("catch ("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistentPackUpdatesUseAnExplicitRestartBoundPath() throws IOException {
|
||||
String managerSource = source("ModdedDimensionManager.java");
|
||||
String createPersistent = method(managerSource, "public static Handle createPersistent(");
|
||||
String update = method(managerSource, "public static UpdateResult stagePersistentUpdate(");
|
||||
|
||||
assertBefore(createPersistent, "if (previous != null)", "ModdedDimensionRegistryStore.put(");
|
||||
assertTrue(createPersistent.contains("Use the Iris world update command"));
|
||||
assertTrue(update.contains("ModdedGenerationHistoryStorage.createOrStage("));
|
||||
assertTrue(update.contains("active.history().pendingActivation()"));
|
||||
assertTrue(update.contains("restartRequired()"));
|
||||
assertFalse(update.contains("inject("));
|
||||
assertFalse(update.contains("repointAndBind("));
|
||||
assertBefore(update, "ModdedDimensionRegistryStore.put(server, updated);",
|
||||
"ModdedGenerationHistoryStorage.createOrStage(");
|
||||
assertTrue(update.contains("ModdedDimensionRegistryStore.put(server, previous);"));
|
||||
|
||||
String commandsSource = source("command/ModdedWorldCommands.java");
|
||||
assertTrue(commandsSource.contains("root.then(updateTree());"));
|
||||
String command = method(commandsSource, "private static int update(");
|
||||
assertTrue(command.contains("ModdedDimensionManager.stagePersistentUpdate("));
|
||||
assertTrue(command.contains("Restart the server to activate it"));
|
||||
assertTrue(command.contains("running world remains on its current pack"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void injectionBindsBeforeRegistrationAndRollsBackEveryFailure() throws IOException {
|
||||
String source = source("ModdedDimensionManager.java");
|
||||
String injection = method(source, "private static Handle inject(");
|
||||
|
||||
assertBefore(injection, "generator.bindLevel(level);", "serverAccess.putLevelIfAbsent(server, key, level);");
|
||||
assertBefore(injection, "serverAccess.putLevelIfAbsent(server, key, level);",
|
||||
"server.getPlayerList().addWorldborderListener(level);");
|
||||
assertFalse(injection.contains("serverAccess.putLevel(server, key, previous);"));
|
||||
String failure = catchBlock(injection);
|
||||
assertBefore(failure, "rollbackInjection(", "throw ");
|
||||
|
||||
String rollback = method(source, "void rollbackInjection(");
|
||||
assertBefore(rollback, "serverAccess.hasLevel(server, key)", "serverAccess.removeLevel(server, key);");
|
||||
assertTrue(rollback.contains("generator.unbindEngine("));
|
||||
assertTrue(rollback.contains("level.close();"));
|
||||
assertTrue(rollback.contains("addSuppressed("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistentReinjectionQuarantinesBrokenEntriesAndContinues() throws IOException {
|
||||
String source = source("ModdedStartup.java");
|
||||
String reinjection = method(source, "private static void reinjectPersistentDimensions(");
|
||||
String failure = catchBlock(reinjection);
|
||||
|
||||
assertTrue(failure.contains("ModdedIrisLog.error("));
|
||||
assertFalse(failure.contains("e.toString()"));
|
||||
assertFalse(failure.contains("throw new IllegalStateException("));
|
||||
assertTrue(reinjection.contains("injected++;"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistentReinjectionWaitsForPlayerServicesAndServerStarted() throws IOException {
|
||||
String bootstrapSource = source("ModdedEngineBootstrap.java");
|
||||
String start = method(bootstrapSource, "public static void start(");
|
||||
String serverAboutToStart = method(bootstrapSource, "public static void serverAboutToStart(");
|
||||
String serverStarted = method(bootstrapSource, "public static void serverStarted(");
|
||||
|
||||
assertFalse(start.contains("ModdedStartup.runOnce(server);"));
|
||||
assertFalse(serverAboutToStart.contains("ModdedStartup.runOnce(server);"));
|
||||
assertEquals(1, occurrences(serverStarted, "ModdedStartup.runOnce(server);"));
|
||||
assertBefore(serverStarted, "bindWorldGenerators(server);", "ModdedStartup.runOnce(server);");
|
||||
assertBefore(serverStarted, "ModdedStartup.runOnce(server);", "reconcileSpawn(server);");
|
||||
|
||||
String startupSource = source("ModdedStartup.java");
|
||||
String runOnce = method(startupSource, "public static void runOnce(");
|
||||
assertBefore(runOnce, "server.getPlayerList() == null", "STARTED.compareAndSet(false, true)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dynamicLevelMutationsInvalidateLoaderTickCaches() throws IOException {
|
||||
String levelsSource = source("ModdedServerLevels.java");
|
||||
String put = method(levelsSource, "public ServerLevel putLevel(");
|
||||
String putIfAbsent = method(levelsSource, "public ServerLevel putLevelIfAbsent(");
|
||||
String remove = method(levelsSource, "public ServerLevel removeLevel(");
|
||||
|
||||
assertBefore(put, "server.levels.put(key, level);", "levelCacheInvalidator.accept(server);");
|
||||
assertTrue(put.contains("if (previous != level)"));
|
||||
assertBefore(putIfAbsent, "server.levels.putIfAbsent(key, level);", "levelCacheInvalidator.accept(server);");
|
||||
assertTrue(putIfAbsent.contains("if (previous == null)"));
|
||||
assertBefore(remove, "server.levels.remove(key);", "levelCacheInvalidator.accept(server);");
|
||||
assertTrue(remove.contains("if (removed != null)"));
|
||||
|
||||
String loaderSource = source("ModdedLoader.java");
|
||||
assertTrue(loaderSource.contains("void invalidateLevelCache(MinecraftServer server);"));
|
||||
String bootstrapSource = source("ModdedEngineBootstrap.java");
|
||||
assertTrue(bootstrapSource.contains("new ModdedServerLevels(boundLoader::invalidateLevelCache)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistentRestoreOpensHistoryBeforeConsultingTheGlobalPack() throws IOException {
|
||||
String source = source("ModdedGenerationHistoryStorage.java");
|
||||
String restore = method(source, "static GenerationHistory restoreOrAdopt(");
|
||||
|
||||
assertBefore(restore, "GenerationHistory.openIfPresent(", "sourceSupplier\").get();");
|
||||
assertTrue(restore.contains("if (existing.isPresent())"));
|
||||
assertBefore(restore, "requireRegistryDefinitions(existing.get()", "return existing.get();");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void productionPackResolutionNeverUsesTheParityProbeAsData() throws IOException {
|
||||
String source = source("ModdedWorldEngines.java");
|
||||
String resolvePack = method(source, "static File resolvePack(");
|
||||
|
||||
assertFalse(source.contains("System.getProperty(\"iris.parity\")"));
|
||||
assertFalse(source.contains("parityPack"));
|
||||
assertFalse(source.contains("falling back to parity pack"));
|
||||
assertTrue(resolvePack.contains("throw new IllegalStateException("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void engineBootstrapResetsStartupExactlyOnceDuringStop() throws IOException {
|
||||
String source = source("ModdedEngineBootstrap.java");
|
||||
String stop = method(source, "public static void stop(");
|
||||
|
||||
assertEquals(1, occurrences(stop, "ModdedStartup::reset"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void engineBootstrapAttemptsEveryShutdownStageAndAggregatesFailures() throws IOException {
|
||||
String source = source("ModdedEngineBootstrap.java");
|
||||
String stop = method(source, "public static void stop(");
|
||||
|
||||
assertBefore(stop, "\"services\"", "\"world engines\"");
|
||||
assertBefore(stop, "\"world engines\"", "\"dimension manager\"");
|
||||
assertBefore(stop, "\"server state\"", "if (failure != null)");
|
||||
assertFalse(stop.contains("throw"));
|
||||
assertTrue(stop.contains("ModdedIrisLog.error(\"Iris modded shutdown completed with failures\", failure);"));
|
||||
|
||||
String runStage = method(source, "private static Throwable runStopStage(");
|
||||
assertTrue(runStage.contains("catch (Throwable stageFailure)"));
|
||||
assertTrue(runStage.contains("failure.addSuppressed(stageFailure);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void worldShutdownRemovesOnlySuccessfullyClosedMappings() throws IOException {
|
||||
String source = source("ModdedWorldEngines.java");
|
||||
String shutdown = method(source, "public static void shutdown()");
|
||||
|
||||
assertTrue(shutdown.contains("new ArrayList<>(ENGINES.entrySet())"));
|
||||
assertFalse(shutdown.contains("ENGINES.clear()"));
|
||||
// Bound generators must go through unbindEngine (latch unloading, then strict evict) so a
|
||||
// late chunk-system drain cannot rebuild an engine nothing closes again.
|
||||
assertTrue(shutdown.contains("generator.unbindEngine(level);"));
|
||||
assertBefore(shutdown, "close(engine);", "ENGINES.remove(level, engine)");
|
||||
assertTrue(shutdown.contains("ENGINES.containsKey(level)"));
|
||||
assertTrue(shutdown.contains("failure.addSuppressed(e);"));
|
||||
assertTrue(shutdown.contains("failed mappings were retained"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void engineEvictionRetainsTheMappingUntilCloseSucceeds() throws IOException {
|
||||
String source = source("ModdedWorldEngines.java");
|
||||
String eviction = method(source, "static void evictOrThrow(");
|
||||
|
||||
assertTrue(eviction.contains("ENGINES.computeIfPresent("));
|
||||
assertFalse(eviction.contains("ENGINES.remove("));
|
||||
assertBefore(eviction, "close(current);", "return null;");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generatorUnbindRetainsOwnershipWhenEvictionFails() throws IOException {
|
||||
String source = source("IrisModdedChunkGenerator.java");
|
||||
String unbind = method(source, "synchronized void unbindEngine(ServerLevel level)");
|
||||
|
||||
assertFalse(unbind.contains("finally"));
|
||||
assertBefore(unbind, "unloading = true;", "ModdedWorldEngines.evictOrThrow(level);");
|
||||
assertBefore(unbind, "ModdedWorldEngines.evictOrThrow(level);", "clearEngineBinding();");
|
||||
String binding = method(source, "private Engine bindEngine(ServerLevel level)");
|
||||
assertTrue(binding.contains("requireBindingAllowed();"));
|
||||
assertTrue(binding.contains("requireCompletedShutdown(cached);"));
|
||||
String bindLevel = method(source, "synchronized void bindLevel(ServerLevel level)");
|
||||
assertBefore(bindLevel, "requireCompletedShutdown(engine);", "unloading = false;");
|
||||
String boundEngine = method(source, "public Engine engineIfBound()");
|
||||
assertTrue(boundEngine.contains("unloading || current == null || current.isClosing() || current.isClosed()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loaderUnloadSurfacesCloseFailureAndDynamicRemovalUsesStrictEviction() throws IOException {
|
||||
String bootstrapSource = source("ModdedEngineBootstrap.java");
|
||||
String unload = method(bootstrapSource, "public static void levelUnloaded(ServerLevel level)");
|
||||
String failure = catchBlock(unload);
|
||||
assertTrue(failure.contains("ModdedIrisLog.error("));
|
||||
assertTrue(failure.contains("throw "));
|
||||
|
||||
String managerSource = source("ModdedDimensionManager.java");
|
||||
String remove = method(managerSource, "public static boolean remove(MinecraftServer server, String dimensionId, boolean wipeStorage)");
|
||||
assertTrue(remove.contains("ModdedWorldEngines.evictOrThrow(level);"));
|
||||
assertFalse(remove.contains("ModdedWorldEngines.evict(level);"));
|
||||
assertTrue(remove.contains("unloadEventStarted = true;"));
|
||||
assertTrue(remove.contains("rollbackRemoval(server, serverAccess, key, level, generator, unloadEventStarted, e);"));
|
||||
|
||||
String rollback = method(managerSource, "private static void rollbackRemoval(");
|
||||
assertTrue(rollback.contains("serverAccess.hasLevel(server, key)"));
|
||||
assertTrue(rollback.contains("generator.bindLevel(level);"));
|
||||
assertTrue(rollback.contains("failure.addSuppressed(rollbackFailure);"));
|
||||
assertTrue(rollback.contains("ModdedIrisLog.error("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void engineBootstrapRollsBackEveryPublishedBindingBeforeRethrowing() throws IOException {
|
||||
String source = source("ModdedEngineBootstrap.java");
|
||||
String bind = method(source, "public static ModdedPlatform bind(");
|
||||
String failure = catchBlock(bind);
|
||||
|
||||
assertBefore(bind, "createdServices.enableAll();", "runtime = new BoundRuntime(");
|
||||
assertTrue(bind.contains("rollback.add(() -> GuiHost.suppressDesktop("));
|
||||
assertTrue(bind.contains("rollback.add(() -> restorePlatform("));
|
||||
assertTrue(bind.contains("rollback.add(() -> ModdedDimensionManager.restoreAccess("));
|
||||
assertTrue(bind.contains("rollback.add(() -> IrisObjectRotation.restorePlatformRotator("));
|
||||
assertTrue(bind.contains("rollback.add(() -> BlockDataMergeSupport.restorePlatformMerger("));
|
||||
assertTrue(bind.contains("rollback.add(() -> TileData.restorePlatformReader("));
|
||||
assertTrue(bind.contains("rollback.add(() -> TileData.restorePlatformFactory("));
|
||||
assertTrue(bind.contains("rollback.add(() -> GuiHost.set("));
|
||||
assertTrue(bind.contains("rollback.add(() -> DecoratorPlatformHooks.restore("));
|
||||
assertTrue(bind.contains("bindService(PreservationRegistry.class"));
|
||||
assertTrue(bind.contains("bindService(EngineEffectsProvider.class"));
|
||||
assertTrue(bind.contains("bindService(EnginePlatformHooks.class"));
|
||||
assertTrue(bind.contains("bindService(EngineWorldManagerProvider.class"));
|
||||
assertTrue(bind.contains("rollback.add(customContentDiscovery::rollback);"));
|
||||
assertBefore(failure, "createdServices.rollback(failure);", "rollback.restore(failure);");
|
||||
assertBefore(failure, "rollback.restore(failure);", "throw ");
|
||||
}
|
||||
|
||||
private static void assertBefore(String source, String first, String second) {
|
||||
int firstIndex = requiredIndex(source, first);
|
||||
int secondIndex = requiredIndex(source, second);
|
||||
assertTrue(first + " must occur before " + second, firstIndex < secondIndex);
|
||||
}
|
||||
|
||||
private static int requiredIndex(String source, String needle) {
|
||||
return requiredIndex(source, needle, 0);
|
||||
}
|
||||
|
||||
private static int requiredIndex(String source, String needle, int start) {
|
||||
int index = source.indexOf(needle, start);
|
||||
assertTrue("Missing source contract token: " + needle, index >= 0);
|
||||
return index;
|
||||
}
|
||||
|
||||
private static String source(String fileName) throws IOException {
|
||||
String sourceRoot = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
assertTrue("Missing system property " + SOURCE_ROOT_PROPERTY,
|
||||
sourceRoot != null && !sourceRoot.isBlank());
|
||||
Path sourcePath = Path.of(sourceRoot, "art", "arcane", "iris", "modded", fileName);
|
||||
return Files.readString(sourcePath);
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = requiredIndex(source, signature);
|
||||
int openBrace = openingBrace(source, start);
|
||||
return source.substring(start, matchingBrace(source, openBrace) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the block of the shallowest catch in the method, i.e. the one attached to the
|
||||
* method's outermost try. Textual first-match would return a nested catch instead, and a
|
||||
* nested catch handles a different failure than the one these contracts pin.
|
||||
*/
|
||||
private static String catchBlock(String source) {
|
||||
int bodyStart = openingBrace(source, 0);
|
||||
int depth = 0;
|
||||
int bestDepth = Integer.MAX_VALUE;
|
||||
int catchIndex = -1;
|
||||
int i = bodyStart;
|
||||
while (i < source.length()) {
|
||||
int skipped = skipNonCode(source, i);
|
||||
if (skipped > i) {
|
||||
i = skipped;
|
||||
continue;
|
||||
}
|
||||
char current = source.charAt(i);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
break;
|
||||
}
|
||||
} else if (current == 'c' && depth < bestDepth && source.startsWith("catch (", i)) {
|
||||
bestDepth = depth;
|
||||
catchIndex = i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
assertTrue("Missing source contract token: catch (", catchIndex >= 0);
|
||||
return blockAt(source, openingBrace(source, catchIndex));
|
||||
}
|
||||
|
||||
private static String blockAt(String source, int openBrace) {
|
||||
return source.substring(openBrace, matchingBrace(source, openBrace) + 1);
|
||||
}
|
||||
|
||||
private static int openingBrace(String source, int start) {
|
||||
int i = start;
|
||||
while (i < source.length()) {
|
||||
int skipped = skipNonCode(source, i);
|
||||
if (skipped > i) {
|
||||
i = skipped;
|
||||
continue;
|
||||
}
|
||||
if (source.charAt(i) == '{') {
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
throw new IllegalArgumentException("No source block after offset " + start);
|
||||
}
|
||||
|
||||
private static int matchingBrace(String source, int openBrace) {
|
||||
int depth = 0;
|
||||
int i = openBrace;
|
||||
while (i < source.length()) {
|
||||
int skipped = skipNonCode(source, i);
|
||||
if (skipped > i) {
|
||||
i = skipped;
|
||||
continue;
|
||||
}
|
||||
char current = source.charAt(i);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source block");
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances past a comment, string, char or text block starting at the index, otherwise returns
|
||||
* the index unchanged. Comments must be skipped: an apostrophe in prose ("generator's") would
|
||||
* otherwise open a char literal and swallow the rest of the file.
|
||||
*/
|
||||
private static int skipNonCode(String source, int index) {
|
||||
char current = source.charAt(index);
|
||||
if (current == '/' && index + 1 < source.length()) {
|
||||
char next = source.charAt(index + 1);
|
||||
if (next == '/') {
|
||||
int end = source.indexOf('\n', index);
|
||||
return end < 0 ? source.length() : end + 1;
|
||||
}
|
||||
if (next == '*') {
|
||||
int end = source.indexOf("*/", index + 2);
|
||||
return end < 0 ? source.length() : end + 2;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
if (current == '"' && source.startsWith("\"\"\"", index)) {
|
||||
int end = source.indexOf("\"\"\"", index + 3);
|
||||
return end < 0 ? source.length() : end + 3;
|
||||
}
|
||||
if (current == '"' || current == '\'') {
|
||||
return endOfLiteral(source, index, current);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private static int endOfLiteral(String source, int index, char terminator) {
|
||||
for (int i = index + 1; i < source.length(); i++) {
|
||||
char current = source.charAt(i);
|
||||
if (current == '\\') {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (current == terminator) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return source.length();
|
||||
}
|
||||
|
||||
private static int occurrences(String source, String needle) {
|
||||
int count = 0;
|
||||
int offset = 0;
|
||||
while (true) {
|
||||
int index = source.indexOf(needle, offset);
|
||||
if (index < 0) {
|
||||
return count;
|
||||
}
|
||||
count++;
|
||||
offset = index + needle.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
-37
@@ -5,13 +5,9 @@ import art.arcane.iris.engine.object.IrisEntitySpawn;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedSpawnerRarityParityTest {
|
||||
private static IrisEntitySpawn spawn(int rarity) {
|
||||
@@ -39,37 +35,4 @@ public class ModdedSpawnerRarityParityTest {
|
||||
assertEquals(1, IRare.get(spawn(-5)));
|
||||
assertEquals(3, IRare.get(spawn(3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void perPositionSpawnLoopsDoNotRerollEntryRarity() throws Exception {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/ModdedWorldManager.java"));
|
||||
|
||||
for (String method : List.of("private int spawnEntry(", "private int spawnEntryAt(")) {
|
||||
String body = methodBody(source, method);
|
||||
assertFalse(method + " must not re-roll rarity per position; rarityPick already weighted the pool",
|
||||
body.contains("getRarity()"));
|
||||
assertTrue(method + " must keep the min/max spawn-count roll",
|
||||
body.contains("LootResolver.inclusive("));
|
||||
}
|
||||
}
|
||||
|
||||
private static String methodBody(String source, String declaration) {
|
||||
int start = source.indexOf(declaration);
|
||||
assertTrue("ModdedWorldManager must declare " + declaration, start >= 0);
|
||||
int open = source.indexOf('{', start);
|
||||
int depth = 0;
|
||||
for (int index = open; index < source.length(); index++) {
|
||||
char character = source.charAt(index);
|
||||
if (character == '{') {
|
||||
depth++;
|
||||
} else if (character == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return source.substring(open + 1, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new AssertionError(declaration + " is not brace balanced");
|
||||
}
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedStructurePlacementFailureContractTest {
|
||||
@Test
|
||||
public void structurePlacementPropagatesRuntimeFailuresWithContext() throws IOException {
|
||||
Path sourcePath = Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/ModdedStructureHooks.java");
|
||||
String source = Files.readString(sourcePath);
|
||||
int methodStart = source.indexOf("public int[] placeStructure(");
|
||||
int methodEnd = source.indexOf("\n @Override", methodStart + 1);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
int catchStart = method.indexOf("catch (RuntimeException error)");
|
||||
|
||||
assertTrue(catchStart >= 0);
|
||||
String failurePath = method.substring(catchStart);
|
||||
assertTrue(failurePath.contains("throw NativeStructureGenerationException.failure("));
|
||||
assertTrue(failurePath.contains("\"capture placement\", structureKey, chunkX, chunkZ, error"));
|
||||
assertFalse(failurePath.contains("return null;"));
|
||||
assertFalse(failurePath.contains("catch (Throwable"));
|
||||
}
|
||||
}
|
||||
+4
-11
@@ -4,8 +4,6 @@ import org.junit.Test;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -64,14 +62,9 @@ public class ModdedStructureTemplateCacheTest {
|
||||
config = new String(input.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
assertTrue(config.contains("\"StructureTemplatePaletteConcurrencyMixin\""));
|
||||
|
||||
Path source = Path.of(System.getProperty("iris.moddedCommonSources"))
|
||||
.resolve("art/arcane/iris/modded/mixin/StructureTemplatePaletteConcurrencyMixin.java");
|
||||
String mixin = Files.readString(source);
|
||||
assertTrue(mixin.contains("@Mixin(StructureTemplate.Palette.class)"));
|
||||
assertTrue(mixin.contains("@Shadow\n @Final\n @Mutable"));
|
||||
assertTrue(mixin.contains("private Map<Block, List<StructureTemplate.StructureBlockInfo>> cache;"));
|
||||
assertTrue(mixin.contains("@Inject(method = \"<init>(Ljava/util/List;)V\", at = @At(\"RETURN\"))"));
|
||||
assertTrue(mixin.contains("cache = new ConcurrentHashMap<>();"));
|
||||
assertNotNull(Class.forName(
|
||||
"art.arcane.iris.modded.mixin.StructureTemplatePaletteConcurrencyMixin",
|
||||
false,
|
||||
ModdedStructureTemplateCacheTest.class.getClassLoader()));
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,6 @@ import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -26,32 +23,6 @@ public class ModdedWorldCheckTest {
|
||||
assertTrue(thread.isDaemon());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void poiAuditRunsInASecondServerTaskAfterVillageGeneration() throws IOException {
|
||||
Path sourceRoot = Path.of(System.getProperty("iris.moddedCommonSources"));
|
||||
String source = Files.readString(sourceRoot.resolve("art/arcane/iris/modded/ModdedWorldCheck.java"));
|
||||
String auditSource = Files.readString(
|
||||
sourceRoot.resolve("art/arcane/iris/modded/WorldCheckStructureAudit.java"));
|
||||
int preparationSubmit = source.indexOf(
|
||||
"WorldCheckPreparation preparation = serverRef.submit(() -> run(serverRef))");
|
||||
int completionSubmit = source.indexOf(
|
||||
"exitCode = serverRef.submit(() -> runAndRequestStop(", preparationSubmit);
|
||||
int completionMethod = source.indexOf("private static boolean completeWorldCheck");
|
||||
int deferredAudit = source.indexOf(
|
||||
"PoiAudit poi = WorldCheckStructureAudit.auditStructurePois", completionMethod);
|
||||
int structureMethod = auditSource.indexOf("private static StructureCheckResult checkNativeStructure");
|
||||
int structureMethodEnd = auditSource.indexOf("private static StructureStart resolveStructureStart",
|
||||
structureMethod);
|
||||
String structureSource = auditSource.substring(structureMethod, structureMethodEnd);
|
||||
|
||||
assertTrue(preparationSubmit >= 0);
|
||||
assertTrue(completionSubmit > preparationSubmit);
|
||||
assertTrue(deferredAudit > completionMethod);
|
||||
assertFalse(structureSource.contains("auditStructurePois"));
|
||||
assertFalse(source.contains("prepareDeferredAudits"));
|
||||
assertFalse(auditSource.contains("prepareDeferredAudits"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validStructureStartIsGenerationEvidence() {
|
||||
assertTrue(WorldCheckPredicates.hasNativeStructureEvidence(true, 0));
|
||||
|
||||
-90
@@ -6,11 +6,6 @@ import net.minecraft.server.Bootstrap;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -22,25 +17,6 @@ public class NativeStructureFailureContractTest {
|
||||
Bootstrap.bootStrap();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structureLocateDoesNotCatchAndFallThroughToAnotherImplementation() throws IOException {
|
||||
String source = moddedSource("IrisModdedChunkGenerator.java");
|
||||
int locateStart = source.indexOf("public Pair<BlockPos, Holder<Structure>> findNearestMapStructure");
|
||||
int locateEnd = source.indexOf("public boolean isNativeStructureReachable", locateStart);
|
||||
String locate = source.substring(locateStart, locateEnd);
|
||||
String stage = moddedSource("ModdedNativeStructureStage.java");
|
||||
int filterStart = stage.indexOf("HolderSet<Structure> filterReachableNativeStructures");
|
||||
int filterEnd = stage.indexOf("void adjustGeneratedStructures", filterStart);
|
||||
String filter = stage.substring(filterStart, filterEnd);
|
||||
|
||||
assertTrue(locate.contains("Engine current = engine();"));
|
||||
assertFalse(locate.contains("catch (Throwable"));
|
||||
assertFalse(locate.contains("return null;\n } catch"));
|
||||
assertTrue(filter.contains("unregistered structure holder"));
|
||||
assertFalse(filter.contains("catch (Throwable"));
|
||||
assertFalse(filter.contains("failed closed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void globalStructureDisableRefusesGeneratorBinding() {
|
||||
IrisModdedChunkGenerator.requireGlobalStructureGeneration(true, "overworld:overworld");
|
||||
@@ -56,66 +32,6 @@ public class NativeStructureFailureContractTest {
|
||||
assertTrue(error.getMessage().contains("importedStructures.disabledExact"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structureVegetationCleanupPrecedesTerrainPreparationAndPlacement() throws IOException {
|
||||
String source = moddedSource("ModdedNativeStructureStage.java");
|
||||
int placementStart = source.indexOf("void placeVanillaStructures");
|
||||
int placementEnd = source.indexOf("private static String nativeStructureBatchContext", placementStart);
|
||||
String placement = source.substring(placementStart, placementEnd);
|
||||
|
||||
assertTrue(placement.contains("\"terrain integration\""));
|
||||
assertTrue(placement.contains("\"terrain preparation\""));
|
||||
assertTrue(placement.contains("\"foundation repair\""));
|
||||
assertFalse(placement.contains("\"terrain carving\""));
|
||||
assertTrue(placement.contains("visitExistingPois(chunk"));
|
||||
assertTrue(placement.contains("level.updatePOIOnBlockStateChange("));
|
||||
assertTrue(placement.contains("Blocks.AIR.defaultBlockState(), state"));
|
||||
assertTrue(placement.indexOf("visitExistingPois(chunk")
|
||||
< placement.indexOf("WorldgenTerrainHeightmaps.primeStructurePlacement("));
|
||||
assertTrue(placement.contains("prepareSurfaceStructures"));
|
||||
assertTrue(placement.contains("clearIntersectingVegetation"));
|
||||
assertTrue(placement.indexOf("clearIntersectingVegetation")
|
||||
< placement.indexOf("prepareSurfaceStructures"));
|
||||
assertTrue(placement.indexOf("prepareSurfaceStructures")
|
||||
< placement.indexOf("for (NativePlacementGroup group"));
|
||||
assertTrue(placement.indexOf("repairVacuumFoundations")
|
||||
> placement.indexOf("for (NativePlacementGroup group"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structurePlacementPrimesNeighbourWorldgenHeightmapsBeforeTerrainPreparation() throws IOException {
|
||||
String source = moddedSource("ModdedNativeStructureStage.java");
|
||||
int placementStart = source.indexOf("void placeVanillaStructures");
|
||||
int placementEnd = source.indexOf("private static String nativeStructureBatchContext", placementStart);
|
||||
String placement = source.substring(placementStart, placementEnd);
|
||||
|
||||
assertTrue(source.contains("import art.arcane.iris.nativegen.WorldgenTerrainHeightmaps;"));
|
||||
assertTrue(placement.contains("WorldgenTerrainHeightmaps.primeStructurePlacement("));
|
||||
assertTrue(placement.contains("\"heightmap priming\""));
|
||||
assertTrue(placement.contains("heightmapStarts.add(start);"));
|
||||
assertTrue(placement.indexOf("WorldgenTerrainHeightmaps.primeStructurePlacement(")
|
||||
< placement.indexOf("prepareSurfaceStructures"));
|
||||
assertTrue(source.contains("Engine.hostHeight(generationEngine, x, z, false) + runtimeMinY + 1"));
|
||||
assertTrue(source.contains("Engine.hostHeight(generationEngine, x, z, true) + runtimeMinY + 1"));
|
||||
assertTrue(placement.contains("current.getDimensionStackContext() != null"));
|
||||
assertTrue(source.contains("int minY = chunk.getMinY() + 1;"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeStructurePostProcessingUsesBoundedWorldgenAccess() throws IOException {
|
||||
String source = moddedSource("ModdedNativeStructureStage.java");
|
||||
int placementStart = source.indexOf("void placeVanillaStructures");
|
||||
int placementEnd = source.indexOf("private List<List<Structure>> structuresByStep", placementStart);
|
||||
String placement = source.substring(placementStart, placementEnd);
|
||||
|
||||
assertTrue(placement.contains("ModdedNativeStructureWorldgenAccess.create("));
|
||||
assertTrue(placement.contains("nativeStructureProtection(current, staticObjects)"));
|
||||
assertTrue(placement.contains("Engine.hostHeight(current, x, z, true)"));
|
||||
assertTrue(placement.contains("placeVanillaStructure(boundedWorld, structureManager"));
|
||||
assertFalse(placement.contains("NativeStructurePostProcessor.place(world,"));
|
||||
assertTrue(placement.contains("world.setCurrentlyGenerating(null);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structureFailurePreservesPhaseIdentityChunkAndCause() {
|
||||
IllegalArgumentException cause = new IllegalArgumentException("broken placement");
|
||||
@@ -128,10 +44,4 @@ public class NativeStructureFailureContractTest {
|
||||
assertTrue(error.getMessage().contains("12,-8"));
|
||||
assertTrue(error.getMessage().contains("aborted"));
|
||||
}
|
||||
|
||||
private static String moddedSource(String fileName) throws IOException {
|
||||
Path sourcePath = Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art", "arcane", "iris", "modded", fileName);
|
||||
return Files.readString(sourcePath);
|
||||
}
|
||||
}
|
||||
-183
@@ -7,9 +7,6 @@ import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.core.BlockPos;
|
||||
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 java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -48,30 +45,6 @@ public class IrisModdedStructureCommandTest {
|
||||
assertEquals(1, nativeReferences.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gotoStructureSupportsIrisAndNativeRegistryTargets() throws IOException {
|
||||
String source = source("ModdedLocateCommands.java");
|
||||
String suggestions = source("ModdedCommandSuggestions.java");
|
||||
|
||||
assertTrue(source.contains("IrisStructureLocator.hasLocatableEditablePlacement(engine, key)"));
|
||||
assertTrue(source.contains("registry.get(identifier)"));
|
||||
assertTrue(source.contains("getPlacementsForStructure(holder)"));
|
||||
assertTrue(source.contains("generator.findNearestMapStructure("));
|
||||
assertTrue(source.contains("NATIVE_STRUCTURE_LOCATE_RADIUS = 100"));
|
||||
assertTrue(source.contains("HolderSet.direct(target.holder())"));
|
||||
assertFalse(source.contains("NativeStructureLocateCapability"));
|
||||
assertTrue(source.contains("boolean teleported = player.teleportTo("));
|
||||
assertTrue(suggestions.contains("combineStructureKeys(unregisteredIrisKeys, nativeKeys)"));
|
||||
assertTrue(source.contains("irisGenerator.isNativeStructureReachable(holder)"));
|
||||
assertTrue(source.contains("LocateStatus.SEARCH_LIMIT_REACHED"));
|
||||
assertTrue(source.contains("IRIS_MODDED_COMMANDS_UNABLE_LOCATE_IRIS_PLACED_STRUCTURE_DENSITY_SEARCH_SAFETY_LIMIT_WAS"));
|
||||
assertTrue(source.contains("int targetX = result.originX()"));
|
||||
assertTrue(source.contains("int targetY = result.baseY() + 2"));
|
||||
assertTrue(source.contains("int targetZ = result.originZ()"));
|
||||
assertFalse(source.contains("at[0] + 8"));
|
||||
assertFalse(source.contains("at[2] + 8"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registeredStructureEligibilityMatchesGotoAndSuggestions() {
|
||||
IrisNativeStructureDecision nativeDecision = decision(NativeStructureGenerationStatus.GENERATE_NATIVE);
|
||||
@@ -175,162 +148,6 @@ public class IrisModdedStructureCommandTest {
|
||||
.contains("non-positive density or a Y band outside"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregisteredCommandUsesGotoEligibilityAndPrintsToConsole() throws IOException {
|
||||
String command = source("ModdedUnregisteredStructures.java");
|
||||
String tree = source("ModdedCommandTree.java");
|
||||
|
||||
assertTrue(tree.contains("Commands.literal(\"unregistered\")"));
|
||||
assertTrue(tree.contains("ModdedUnregisteredStructures.print(context.getSource())"));
|
||||
assertTrue(command.contains("ModdedCommandSuggestions.isEligibleRegisteredStructure("));
|
||||
assertTrue(command.contains("ModdedLocateCommands.registeredStructureUnavailableMessage("));
|
||||
assertTrue(command.contains("engine.getData().getStructureLoader().getPossibleKeys()"));
|
||||
assertTrue(command.contains("IrisStructureLocator.hasLocatableEditablePlacement(engine, key)"));
|
||||
assertTrue(command.contains("ModdedIrisLog.info(\"[Iris goto unregistered] [{}] {} - {}\""));
|
||||
assertTrue(command.contains("UNREGISTERED(\"unregistered\")"));
|
||||
assertFalse(command.contains("DatapackIngestService"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structureSuggestionsUseActiveLevelAvailability() throws IOException {
|
||||
String suggestions = source("ModdedCommandSuggestions.java");
|
||||
int methodStart = suggestions.indexOf("static CompletableFuture<Suggestions> suggestStructureKeys(");
|
||||
int methodEnd = suggestions.indexOf("static boolean isEligibleRegisteredStructure(", methodStart);
|
||||
String method = suggestions.substring(methodStart, methodEnd);
|
||||
|
||||
assertTrue(method.contains("ServerLevel level = source.getLevel()"));
|
||||
assertTrue(method.contains("Engine engine = IrisModdedCommands.engineFor(level)"));
|
||||
assertFalse(method.contains("IrisStructureLocator.locatableKeys(engine)"));
|
||||
assertTrue(method.contains("IrisStructureLocator.locatableEditableKeys(engine)"));
|
||||
assertTrue(method.contains("StructureReachability.reachableKeys(engine)"));
|
||||
assertTrue(method.contains("NativeStructureGenerationPolicy.resolve(engine, key, false)"));
|
||||
assertTrue(method.contains("IrisStructureLocator.hasLocatableNativePlacement(engine, key)"));
|
||||
assertTrue(method.contains("IrisStructureLocator.hasLocatableEditablePlacement(engine, key)"));
|
||||
assertTrue(method.contains("reachableNativeKeys.contains(normalizeKey(key))"));
|
||||
assertTrue(method.contains("nativeGenerationEnabled"));
|
||||
assertTrue(method.contains("eligibleUnregisteredEditableKeys("));
|
||||
assertTrue(method.contains("IrisStructureLocator.hasNativePlacement(engine, candidate)"));
|
||||
assertFalse(method.contains("NativeStructureGenerationKeys.active(level)"));
|
||||
assertFalse(method.contains("getPlacementsForStructure"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generatorLocateUsesEveryIrisPlacedNativeStructure() throws IOException {
|
||||
String source = moddedSource("ModdedNativeStructureStage.java");
|
||||
int methodStart = source.indexOf("Pair<BlockPos, Holder<Structure>> findNearestIrisStructure(");
|
||||
int methodEnd = source.indexOf("HolderSet<Structure> filterReachableNativeStructures(", methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
int registryLookup = method.indexOf("level.registryAccess().lookupOrThrow(Registries.STRUCTURE)");
|
||||
int placedCheck = method.indexOf(
|
||||
"if (!IrisStructureLocator.hasNativePlacement(current, structureId))");
|
||||
int irisLocate = method.indexOf("NativeStructureLocatePersistence.search(", placedCheck);
|
||||
int nearestSelection = method.indexOf(
|
||||
"NativeStructureLocateResults.nearest(pos, predicted, nativeLocated)", irisLocate);
|
||||
int selectedVerification = method.indexOf("bestSearch.search().verify(bestResult)", nearestSelection);
|
||||
int selectedReference = method.indexOf(
|
||||
"selectedSearch.search().reference(selectedStart)", selectedVerification);
|
||||
|
||||
assertTrue(registryLookup >= 0);
|
||||
assertTrue(placedCheck > registryLookup);
|
||||
assertTrue(irisLocate > placedCheck);
|
||||
assertTrue(nearestSelection > irisLocate);
|
||||
assertTrue(selectedVerification > nearestSelection);
|
||||
assertTrue(selectedReference > selectedVerification);
|
||||
assertTrue(method.contains("NativeStructureLocatePersistence.probe("));
|
||||
assertTrue(method.contains("findUnexplored"));
|
||||
assertTrue(method.contains("LocateStatus.SEARCH_LIMIT_REACHED"));
|
||||
assertTrue(method.contains("verified.ownership().locatorY()"));
|
||||
assertTrue(method.contains("selectedSearch.search().reference(selectedStart)"));
|
||||
assertTrue(method.contains("NativeStructureLocateResults.selectAndReference("));
|
||||
assertFalse(method.contains("NativeStructureLocateCapability"));
|
||||
assertTrue(source.contains("structureBiomeSource.isStructureReachable(holder)"));
|
||||
assertFalse(source.contains("isPaperUnavailable"));
|
||||
String generator = moddedSource("IrisModdedChunkGenerator.java");
|
||||
assertTrue(generator.contains("NativeStructureVanillaLocator.predict("));
|
||||
assertFalse(generator.contains("super.findNearestMapStructure(level, reachable"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void commandResolvesNativePolicyBeforeAnyVanillaAliasLookup() throws IOException {
|
||||
String source = source("ModdedLocateCommands.java");
|
||||
int methodStart = source.indexOf("static int gotoStructure(");
|
||||
int methodEnd = source.indexOf("private static void locateIrisStructure(", methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
int nativeResolution = method.indexOf("resolveNativeStructure(source, level, engine, key)");
|
||||
int editableIrisLookup = method.indexOf(
|
||||
"IrisStructureLocator.hasLocatableEditablePlacement(engine, key)", nativeResolution);
|
||||
int policyResolution = method.indexOf(
|
||||
"NativeStructureGenerationPolicy.resolve(engine, target.key(), false)", editableIrisLookup);
|
||||
int eligibilityCheck = method.indexOf(
|
||||
"ModdedCommandSuggestions.isEligibleRegisteredStructure(", policyResolution);
|
||||
int replacementLocate = method.indexOf(
|
||||
"locateIrisStructure(source, level, engine, player, target.key())", eligibilityCheck);
|
||||
int nativeLocate = method.indexOf("runNativeStructureLocate(source, level, player, target)",
|
||||
replacementLocate);
|
||||
|
||||
assertTrue(nativeResolution >= 0);
|
||||
assertTrue(editableIrisLookup > nativeResolution);
|
||||
assertTrue(policyResolution > editableIrisLookup);
|
||||
assertTrue(eligibilityCheck > policyResolution);
|
||||
assertTrue(replacementLocate > eligibilityCheck);
|
||||
assertTrue(nativeLocate > replacementLocate);
|
||||
assertTrue(method.contains("IrisStructureLocator.hasLocatableNativePlacement(engine, target.key())"));
|
||||
assertTrue(method.contains("IrisStructureLocator.hasLocatableEditablePlacement(engine, target.key())"));
|
||||
assertTrue(method.contains("!IrisStructureLocator.hasNativePlacement(engine, key)"));
|
||||
assertTrue(method.contains("StructureReachability.isReachable(engine, target.key())"));
|
||||
assertTrue(method.contains("getWorldGenSettings().options().generateStructures()"));
|
||||
assertFalse(method.contains("IrisStructureLocator.isPlaced(engine, key)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyResolvesRegisteredNativeBeforeGenericIrisAliases() throws IOException {
|
||||
String source = source("ModdedLocateCommands.java");
|
||||
int methodStart = source.indexOf("private static int verifyStructure(");
|
||||
int methodEnd = source.indexOf("private static Optional<NativeStructureTarget> resolveNativeStructure(",
|
||||
methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
int nativeResolution = method.indexOf("resolveNativeStructure(source, level, engine, key)");
|
||||
int genericIrisLookup = method.indexOf("IrisStructureLocator.isPlaced(engine, key)", nativeResolution);
|
||||
|
||||
assertTrue(nativeResolution >= 0);
|
||||
assertTrue(genericIrisLookup > nativeResolution);
|
||||
assertTrue(method.contains("NativeStructureAvailability.IRIS_SUPPRESSED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structureVerificationNoLongerClaimsNativeGenerationIsMissing() throws IOException {
|
||||
String source = source("ModdedStructureCommands.java");
|
||||
|
||||
assertTrue(source.contains("verifyTree(\"verify\")"));
|
||||
assertTrue(source.contains("verifyTree(\"locateall\")"));
|
||||
assertTrue(source.contains("IrisModdedCommands.verifyStructures("));
|
||||
assertFalse(source.contains("Vanilla structure locate is meaningless here"));
|
||||
}
|
||||
|
||||
private String source(String fileName) throws IOException {
|
||||
Path source = commonSourceRoot().resolve("art/arcane/iris/modded/command/")
|
||||
.resolve(fileName)
|
||||
.normalize();
|
||||
return Files.readString(source);
|
||||
}
|
||||
|
||||
private String moddedSource(String fileName) throws IOException {
|
||||
Path source = commonSourceRoot().resolve("art/arcane/iris/modded/")
|
||||
.resolve(fileName)
|
||||
.normalize();
|
||||
return Files.readString(source);
|
||||
}
|
||||
|
||||
private Path commonSourceRoot() {
|
||||
String configuredRoot = System.getProperty("iris.moddedCommonSources");
|
||||
if (configuredRoot != null && !configuredRoot.isBlank()) {
|
||||
return Path.of(configuredRoot);
|
||||
}
|
||||
return Path.of(System.getProperty("user.dir"))
|
||||
.resolve("../modded-common/src/main/java")
|
||||
.normalize();
|
||||
}
|
||||
|
||||
private IrisNativeStructureDecision decision(NativeStructureGenerationStatus status) {
|
||||
return new IrisNativeStructureDecision(status, 0, null, false, null, null);
|
||||
}
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package art.arcane.iris.modded.command;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public final class ModdedRiverLocateContractTest {
|
||||
@Test
|
||||
public void riverLookupMergesRecordedAndEligibleActiveFeatures() throws IOException {
|
||||
Path sourcePath = Path.of(System.getProperty("iris.moddedCommonSources"))
|
||||
.resolve("art/arcane/iris/modded/command/ModdedLocateCommands.java");
|
||||
String source = Files.readString(sourcePath);
|
||||
int methodStart = source.indexOf("static int gotoRiver(");
|
||||
int methodEnd = source.indexOf("static int gotoStructure(", methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
|
||||
assertTrue(method.contains("GenerationSemanticQueries.nearestRiver("));
|
||||
assertTrue(method.contains("runtime == null"));
|
||||
assertTrue(method.contains("feature.y() + 2"));
|
||||
assertFalse(method.contains("runtime.nearestFeature("));
|
||||
assertFalse(method.contains("feature.y() + engine.getDimension().getMinHeight()"));
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package art.arcane.iris.modded.command;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public final class ModdedStructureHistoryContractTest {
|
||||
@Test
|
||||
public void retainedStructureNamesReachTheSemanticSearchOnBothAdmissionBranches() throws IOException {
|
||||
Path root = Path.of(System.getProperty("iris.moddedCommonSources"));
|
||||
String source = Files.readString(root.resolve("art/arcane/iris/modded/command/ModdedLocateCommands.java"));
|
||||
int start = source.indexOf("static int gotoStructure(");
|
||||
int end = source.indexOf("static String registeredStructureUnavailableMessage(", start);
|
||||
String admission = source.substring(start, end);
|
||||
String retainedCheck = "GenerationFindCatalog.hasRetainedStructurePlacement(engine, key)";
|
||||
int firstCheck = admission.indexOf(retainedCheck);
|
||||
assertTrue(firstCheck >= 0);
|
||||
assertTrue(admission.indexOf(retainedCheck, firstCheck + retainedCheck.length()) >= 0);
|
||||
|
||||
int locateStart = source.indexOf("private static void locateIrisStructure(");
|
||||
int locateEnd = source.indexOf("private static void runNativeStructureLocate(", locateStart);
|
||||
String locate = source.substring(locateStart, locateEnd);
|
||||
assertTrue(locate.contains("GenerationSemanticQueries.nearestStructure("));
|
||||
assertFalse(locate.contains("IrisStructureLocator.locate("));
|
||||
|
||||
String suggestions = Files.readString(root.resolve("art/arcane/iris/modded/command/ModdedCommandSuggestions.java"));
|
||||
assertTrue(suggestions.contains("GenerationFindCatalog.retainedStructureKeys(engine)"));
|
||||
}
|
||||
}
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
package art.arcane.iris.modded.command;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedStudioTeleportContractTest {
|
||||
private static final String SOURCE_ROOT_PROPERTY = "iris.moddedCommonSources";
|
||||
|
||||
@Test
|
||||
public void studioOpenSerializesReplacementAndWaitsForTheNativePath() throws IOException {
|
||||
String source = source("command/ModdedStudioCommands.java");
|
||||
String open = method(source, "private static int open(");
|
||||
String execute = method(source, "private static void executeStudioOpen(");
|
||||
|
||||
assertTrue(open.contains("TRANSITIONS.submit("));
|
||||
assertFalse(open.contains("orTimeout("));
|
||||
assertFalse(open.contains("deadlineNanos"));
|
||||
assertBefore(execute, "replaceExistingStudio(", "ModdedDimensionManager.createTransientStudio(");
|
||||
assertBefore(execute, "ModdedDimensionManager.createTransientStudio(", "ModdedDimensionManager.teleportAsync(");
|
||||
assertFalse(execute.contains("deadlineNanos"));
|
||||
assertFalse(source.contains("player.teleportTo(studio"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void studioTeleportAndVisionShareWarmFutureSemantics() throws IOException {
|
||||
String studio = source("command/ModdedStudioCommands.java");
|
||||
String vision = source("command/ModdedVisionOverlay.java");
|
||||
String dimensions = source("ModdedDimensionManager.java");
|
||||
|
||||
assertTrue(studio.contains("private static CompletableFuture<Void> teleportToStudio("));
|
||||
assertTrue(studio.contains("ModdedDimensionManager.teleportAsync("));
|
||||
assertTrue(dimensions.contains("TELEPORT_WARM_RADIUS = 0"));
|
||||
assertTrue(dimensions.contains("addTicketAndLoadWithRadius(\n"
|
||||
+ " TELEPORT_WARM_TICKET,\n"
|
||||
+ " chunkPos,\n"
|
||||
+ " TELEPORT_WARM_RADIUS)"));
|
||||
assertTrue(dimensions.contains("removeTicketWithRadius(\n"
|
||||
+ " TELEPORT_WARM_TICKET,\n"
|
||||
+ " chunkPos,\n"
|
||||
+ " TELEPORT_WARM_RADIUS)"));
|
||||
assertTrue(vision.contains("Math.floor(worldX)"));
|
||||
assertTrue(vision.contains("Math.floor(worldZ)"));
|
||||
assertTrue(vision.contains("if (opener != null)"));
|
||||
assertTrue(vision.contains("ModdedDimensionManager.teleportAsync("));
|
||||
assertFalse(vision.contains("level.getHeight("));
|
||||
assertFalse(vision.contains("player.teleportTo("));
|
||||
}
|
||||
|
||||
private static String source(String relative) throws IOException {
|
||||
String root = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
if (root == null || root.isBlank()) {
|
||||
throw new IllegalStateException("Missing test property " + SOURCE_ROOT_PROPERTY);
|
||||
}
|
||||
return Files.readString(Path.of(root).resolve("art/arcane/iris/modded").resolve(relative))
|
||||
.replace("\r\n", "\n");
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
if (start < 0) {
|
||||
throw new IllegalArgumentException("Missing source method " + signature);
|
||||
}
|
||||
int open = source.indexOf('{', start);
|
||||
int depth = 0;
|
||||
for (int index = open; index < source.length(); index++) {
|
||||
char current = source.charAt(index);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}' && --depth == 0) {
|
||||
return source.substring(start, index + 1);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source method " + signature);
|
||||
}
|
||||
|
||||
private static void assertBefore(String source, String first, String second) {
|
||||
int firstIndex = source.indexOf(first);
|
||||
int secondIndex = source.indexOf(second);
|
||||
assertTrue("Missing source token " + first, firstIndex >= 0);
|
||||
assertTrue("Missing source token " + second, secondIndex >= 0);
|
||||
assertTrue(first + " must precede " + second, firstIndex < secondIndex);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -2,6 +2,7 @@ package art.arcane.iris.modded.localization;
|
||||
|
||||
import art.arcane.iris.core.localization.IrisLanguage;
|
||||
import art.arcane.iris.core.localization.IrisMessages;
|
||||
import art.arcane.iris.testsupport.ProjectPaths;
|
||||
import art.arcane.volmlib.util.localization.MessageKey;
|
||||
import art.arcane.volmlib.util.localization.VolmitLocales;
|
||||
import com.google.gson.JsonObject;
|
||||
@@ -27,7 +28,7 @@ public class ModdedMessageContributorTest {
|
||||
|
||||
@Test
|
||||
public void completeLoaderCatalogStillMatchesEveryDownloadableLocale() throws Exception {
|
||||
Path sourceRoot = Path.of("../../core/src/main/resources/languages");
|
||||
Path sourceRoot = ProjectPaths.repositoryFile("core/src/main/resources/languages");
|
||||
for (String locale : VolmitLocales.nonEnglish()) {
|
||||
JsonObject document = JsonParser.parseString(Files.readString(sourceRoot.resolve(locale + ".json")))
|
||||
.getAsJsonObject();
|
||||
|
||||
Reference in new issue
Block a user