mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-07-17 04:13:46 +00:00
Fixes
This commit is contained in:
+18
-6
@@ -70,10 +70,10 @@ public class CustomBiomeSource extends BiomeSource {
|
||||
LinkedHashSet<Holder<Biome>> biomes = new LinkedHashSet<>();
|
||||
|
||||
for (IrisBiome i : engine.getAllBiomes()) {
|
||||
Holder<Biome> vanillaHolder = NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative());
|
||||
Holder<Biome> vanillaHolder = resolveBiomeHolder(registry, i.getStructureDerivativeKey());
|
||||
if (vanillaHolder == null) {
|
||||
throw new IllegalStateException("Iris structure biome derivative '"
|
||||
+ i.getVanillaDerivativeKey() + "' is not registered for biome '" + i.getLoadKey() + "'");
|
||||
+ i.getStructureDerivativeKey() + "' is not registered for biome '" + i.getLoadKey() + "'");
|
||||
}
|
||||
biomes.add(vanillaHolder);
|
||||
|
||||
@@ -312,10 +312,10 @@ public class CustomBiomeSource extends BiomeSource {
|
||||
throw new IllegalStateException("Iris returned no surface structure biome at block "
|
||||
+ blockX + "," + blockZ);
|
||||
}
|
||||
Holder<Biome> holder = NMSBinding.biomeToBiomeBase(biomeRegistry, irisBiome.getVanillaDerivative());
|
||||
Holder<Biome> holder = resolveBiomeHolder(biomeRegistry, irisBiome.getStructureDerivativeKey());
|
||||
if (holder == null) {
|
||||
throw new IllegalStateException("Iris structure biome derivative '"
|
||||
+ irisBiome.getVanillaDerivativeKey() + "' is not registered at block "
|
||||
+ irisBiome.getStructureDerivativeKey() + "' is not registered at block "
|
||||
+ blockX + "," + blockZ);
|
||||
}
|
||||
return holder;
|
||||
@@ -371,10 +371,11 @@ public class CustomBiomeSource extends BiomeSource {
|
||||
+ x + "," + y + "," + z);
|
||||
}
|
||||
|
||||
Holder<Biome> holder = NMSBinding.biomeToBiomeBase(biomeRegistry, resolution.irisBiome.getVanillaDerivative());
|
||||
Holder<Biome> holder = resolveBiomeHolder(
|
||||
biomeRegistry, resolution.irisBiome.getStructureDerivativeKey());
|
||||
if (holder == null) {
|
||||
throw new IllegalStateException("Iris structure biome derivative '"
|
||||
+ resolution.irisBiome.getVanillaDerivativeKey() + "' is not registered at block "
|
||||
+ resolution.irisBiome.getStructureDerivativeKey() + "' is not registered at block "
|
||||
+ resolution.blockX + "," + resolution.blockY + "," + resolution.blockZ);
|
||||
}
|
||||
return holder;
|
||||
@@ -505,6 +506,17 @@ public class CustomBiomeSource extends BiomeSource {
|
||||
return optionalReferenceHolder.get();
|
||||
}
|
||||
|
||||
private static Holder<Biome> resolveBiomeHolder(Registry<Biome> registry, String biomeKey) {
|
||||
if (registry == null || biomeKey == null || biomeKey.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
Identifier identifier = Identifier.tryParse(biomeKey);
|
||||
if (identifier == null) {
|
||||
return null;
|
||||
}
|
||||
return registry.get(ResourceKey.create(Registries.BIOME, identifier)).orElse(null);
|
||||
}
|
||||
|
||||
private static Holder<Biome> resolveFallbackBiome(Registry<Biome> registry, Registry<Biome> customRegistry) {
|
||||
Holder<Biome> plains = NMSBinding.biomeToBiomeBase(registry, org.bukkit.block.Biome.PLAINS);
|
||||
if (plains != null) {
|
||||
|
||||
+1
-2
@@ -3,7 +3,6 @@ package art.arcane.iris.core.nms.v26_2_R1;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.framework.IrisStructureLocator;
|
||||
import art.arcane.iris.engine.framework.NativeStructureGenerationPolicy;
|
||||
import art.arcane.iris.core.structure.NativeStructureLocateCapability;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.engine.object.IrisNativeStructureDecision;
|
||||
import art.arcane.iris.engine.object.IrisStructureStiltSettings;
|
||||
@@ -174,7 +173,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
String key = id.toString();
|
||||
IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve(engine,
|
||||
key, NativeStructurePostProcessor.isUndergroundStep(holder.value().step()));
|
||||
if (NativeStructureLocateCapability.isPaperUnavailable(key) || !decision.generate()) {
|
||||
if (!decision.generate()) {
|
||||
continue;
|
||||
}
|
||||
candidates.add(new NativeLocateCandidate(holder, key));
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package art.arcane.iris.core.nms.v26_2_R1;
|
||||
|
||||
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 CustomBiomeSourceStructureContractTest {
|
||||
@Test
|
||||
public void nativeStructuresUseTerrainSafeDerivativeAtEveryBiomeBoundary() throws IOException {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.customBiomeSource")));
|
||||
|
||||
assertTrue(source.contains("resolveBiomeHolder(registry, i.getStructureDerivativeKey())"));
|
||||
assertTrue(source.contains("resolveBiomeHolder(biomeRegistry, irisBiome.getStructureDerivativeKey())"));
|
||||
assertTrue(source.contains("resolution.irisBiome.getStructureDerivativeKey()"));
|
||||
assertFalse(source.contains("resolution.irisBiome.getVanillaDerivative()"));
|
||||
}
|
||||
}
|
||||
+8
-7
@@ -17,7 +17,7 @@ import static org.junit.Assert.assertSame;
|
||||
|
||||
public class IrisChunkGeneratorMonumentLocateContractTest {
|
||||
@Test
|
||||
public void nativeLocateAllowsExplicitReplacementBeforeNativeCapabilityGate() throws IOException {
|
||||
public void nativeLocateAllowsMonumentsAfterPolicyAndReachabilityChecks() throws IOException {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.nmsChunkGeneratorSource")));
|
||||
int findStart = source.indexOf("findNearestMapStructure(ServerLevel level");
|
||||
assertTrue(findStart >= 0);
|
||||
@@ -42,9 +42,9 @@ public class IrisChunkGeneratorMonumentLocateContractTest {
|
||||
int reachabilityStart = source.indexOf("private Set<String> reachableStructureKeys", filterStart);
|
||||
assertTrue(reachabilityStart > filterStart);
|
||||
String filterMethod = source.substring(filterStart, reachabilityStart);
|
||||
int monumentReject = filterMethod.indexOf("if (NativeStructureLocateCapability.isPaperUnavailable(key)");
|
||||
int rejectContinue = filterMethod.indexOf("continue;", monumentReject);
|
||||
int emptyNativePartition = filterMethod.indexOf("if (candidates.isEmpty())", rejectContinue);
|
||||
int policyFilter = filterMethod.indexOf("if (!decision.generate())");
|
||||
int filterContinue = filterMethod.indexOf("continue;", policyFilter);
|
||||
int emptyNativePartition = filterMethod.indexOf("if (candidates.isEmpty())", filterContinue);
|
||||
int reachabilityLookup = filterMethod.indexOf("reachableStructureKeys(level)", emptyNativePartition);
|
||||
|
||||
assertTrue(policyResolution >= 0);
|
||||
@@ -58,10 +58,11 @@ public class IrisChunkGeneratorMonumentLocateContractTest {
|
||||
assertTrue(nativeFilter >= 0);
|
||||
assertTrue(delegateLocate > nativeFilter);
|
||||
assertTrue(nearestSelection > delegateLocate);
|
||||
assertTrue(monumentReject >= 0);
|
||||
assertTrue(rejectContinue > monumentReject);
|
||||
assertTrue(emptyNativePartition > rejectContinue);
|
||||
assertTrue(policyFilter >= 0);
|
||||
assertTrue(filterContinue > policyFilter);
|
||||
assertTrue(emptyNativePartition > filterContinue);
|
||||
assertTrue(reachabilityLookup > emptyNativePartition);
|
||||
assertFalse(filterMethod.contains("NativeStructureLocateCapability"));
|
||||
assertFalse(irisHelper.contains("NativeStructureLocateCapability.isPaperUnavailable(structureId)"));
|
||||
assertTrue(irisHelper.contains("new BlockPos(result.originX(), result.baseY(), result.originZ())"));
|
||||
}
|
||||
|
||||
+29
-4
@@ -173,6 +173,24 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(raised, 0, 68, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void raisedSurfaceTerrainDoesNotSliceTreeBlocks() {
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
BlockState log = Blocks.OAK_LOG.defaultBlockState();
|
||||
BlockState leaves = Blocks.OAK_LEAVES.defaultBlockState();
|
||||
put(blocks, 0, 63, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, 0, 64, 0, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
put(blocks, 0, 66, 0, log);
|
||||
put(blocks, 0, 68, 0, leaves);
|
||||
|
||||
NativeStructurePostProcessor.applySurfaceColumn(
|
||||
world(blocks), new BlockPos.MutableBlockPos(),
|
||||
0, 0, 64, 68, -64, 319);
|
||||
|
||||
assertEquals(log, state(blocks, 0, 66, 0));
|
||||
assertEquals(leaves, state(blocks, 0, 68, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loweredFluidColumnsRemainFluidFilled() {
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
@@ -266,11 +284,18 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vegetationCleanupUsesTheSameCircularTaperAsTerrain() {
|
||||
BoundingBox piece = new BoundingBox(0, 60, 0, 4, 80, 4);
|
||||
public void templateAirDoesNotEraseTreesInsideVillagePieces() throws Exception {
|
||||
BlockPos origin = new BlockPos(0, 80, 0);
|
||||
StructureTemplate template = template(List.of(
|
||||
new StructureTemplate.StructureBlockInfo(BlockPos.ZERO, Blocks.AIR.defaultBlockState(), null)));
|
||||
StructurePlaceSettings settings = new StructurePlaceSettings().setRotation(Rotation.NONE);
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
BlockState log = Blocks.OAK_LOG.defaultBlockState();
|
||||
blocks.put(origin, log);
|
||||
|
||||
assertTrue(NativeStructurePostProcessor.withinSurfaceTerrainRadius(16, 2, piece, 12));
|
||||
assertFalse(NativeStructurePostProcessor.withinSurfaceTerrainRadius(16, 16, piece, 12));
|
||||
NativeStructurePostProcessor.clearTemplateAir(world(blocks), template, origin, 80, settings);
|
||||
|
||||
assertEquals(log, blocks.get(origin));
|
||||
}
|
||||
|
||||
private static NativeStructurePostProcessor.SurfaceAnchor anchor(int meetY, int strength) {
|
||||
|
||||
+2
-2
@@ -27,8 +27,8 @@ public class NativeStructurePostProcessorVegetationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void surfaceStructuresClearTheirEntireFootprintByDefault() {
|
||||
assertTrue(NativeStructurePostProcessor.shouldClearEntireVegetationFootprint(
|
||||
public void surfaceStructuresPreserveVegetationUnlessConfigured() {
|
||||
assertFalse(NativeStructurePostProcessor.shouldClearEntireVegetationFootprint(
|
||||
GenerationStep.Decoration.SURFACE_STRUCTURES, false));
|
||||
assertTrue(NativeStructurePostProcessor.shouldClearEntireVegetationFootprint(
|
||||
GenerationStep.Decoration.SURFACE_STRUCTURES, true));
|
||||
|
||||
@@ -21,7 +21,6 @@ package art.arcane.iris.core.commands;
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.platform.bukkit.BukkitPlatform;
|
||||
import art.arcane.iris.core.service.ObjectStudioSaveService;
|
||||
import art.arcane.iris.core.structure.NativeStructureLocateCapability;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.framework.IrisStructureLocator;
|
||||
import art.arcane.iris.engine.framework.NativeStructureGenerationPolicy;
|
||||
@@ -136,10 +135,6 @@ public class CommandFind implements DirectorExecutor {
|
||||
locateIrisStructure(e, structureKey, commandSender);
|
||||
return;
|
||||
}
|
||||
if (NativeStructureLocateCapability.isPaperUnavailable(structureKey)) {
|
||||
commandSender.sendMessage(C.RED + NativeStructureLocateCapability.unavailableMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (nativeStructure == null && IrisStructureLocator.isPlaced(e, structureKey)) {
|
||||
|
||||
-9
@@ -21,7 +21,6 @@ package art.arcane.iris.core.commands;
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.core.structure.BulkStructureImporter;
|
||||
import art.arcane.iris.core.structure.NativeStructureLocateCapability;
|
||||
import art.arcane.iris.core.structure.StructureCaptureImporter;
|
||||
import art.arcane.iris.core.structure.StructureImporter;
|
||||
import art.arcane.iris.core.structure.StructureIndexService;
|
||||
@@ -185,7 +184,6 @@ public class CommandStructure implements DirectorExecutor {
|
||||
int nativeEligible = 0;
|
||||
int disabled = 0;
|
||||
int unreachable = 0;
|
||||
int unavailable = 0;
|
||||
int searchLimited = 0;
|
||||
int errors = 0;
|
||||
for (String keyName : structureKeys) {
|
||||
@@ -228,19 +226,12 @@ public class CommandStructure implements DirectorExecutor {
|
||||
continue;
|
||||
}
|
||||
nativeEligible++;
|
||||
if (NativeStructureLocateCapability.isPaperUnavailable(keyName)) {
|
||||
unavailable++;
|
||||
messages.add(C.YELLOW + "[native-eligible/locate-unavailable] " + C.WHITE + keyName + C.YELLOW + ": "
|
||||
+ NativeStructureLocateCapability.unavailableMessage());
|
||||
continue;
|
||||
}
|
||||
messages.add(C.GREEN + "[native-eligible] " + C.WHITE + keyName);
|
||||
}
|
||||
messages.add(C.GREEN + "Structure verify: " + C.WHITE + located + C.GREEN + " Iris placements located, "
|
||||
+ C.WHITE + nativeEligible + C.GREEN + " native structures eligible, "
|
||||
+ C.WHITE + disabled + C.GREEN + " disabled by policy, "
|
||||
+ C.WHITE + unreachable + C.GREEN + " biome-unreachable, "
|
||||
+ C.WHITE + unavailable + C.GREEN + " generation-eligible but unavailable to synchronous locate, "
|
||||
+ C.WHITE + searchLimited + C.GREEN + " density searches safety-limited, "
|
||||
+ C.WHITE + errors + C.GREEN + " errors. Native eligibility is checked without running blocking live locates.");
|
||||
sendVerificationMessages(commandSender, target, messages);
|
||||
|
||||
+3
-4
@@ -35,18 +35,17 @@ public class IrisStructureLocateCommandContractTest {
|
||||
int replacementCheck = method.indexOf(
|
||||
"decision.status() == NativeStructureGenerationStatus.REPLACED_BY_IRIS", policyResolution);
|
||||
int replacementLocate = method.indexOf("locateIrisStructure(e, structureKey, commandSender)", replacementCheck);
|
||||
int capabilityCheck = method.indexOf("NativeStructureLocateCapability.isPaperUnavailable(structureKey)", replacementLocate);
|
||||
int genericIrisLookup = method.indexOf(
|
||||
"nativeStructure == null && IrisStructureLocator.isPlaced(e, structureKey)", capabilityCheck);
|
||||
"nativeStructure == null && IrisStructureLocator.isPlaced(e, structureKey)", replacementLocate);
|
||||
int nativeLocate = method.indexOf("targetWorld.locateNearestStructure(", genericIrisLookup);
|
||||
assertTrue(nativeResolution >= 0);
|
||||
assertTrue(policyResolution > nativeResolution);
|
||||
assertTrue(replacementCheck > policyResolution);
|
||||
assertTrue(replacementLocate > replacementCheck);
|
||||
assertTrue(capabilityCheck > replacementLocate);
|
||||
assertTrue(genericIrisLookup > capabilityCheck);
|
||||
assertTrue(genericIrisLookup > replacementLocate);
|
||||
assertTrue(nativeLocate > policyResolution);
|
||||
assertTrue(method.contains("decision.status() != NativeStructureGenerationStatus.REPLACED_BY_IRIS"));
|
||||
assertFalse(method.contains("NativeStructureLocateCapability"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+25
-22
@@ -7,6 +7,7 @@ import art.arcane.iris.engine.object.IrisStructureStiltSettings;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
@@ -252,7 +253,7 @@ public final class NativeStructurePostProcessor {
|
||||
|
||||
public static boolean shouldClearEntireVegetationFootprint(GenerationStep.Decoration step,
|
||||
boolean configured) {
|
||||
return configured || !isUndergroundStep(step);
|
||||
return configured;
|
||||
}
|
||||
|
||||
public static void prepareSurfaceStructures(WorldGenLevel world, BoundingBox area,
|
||||
@@ -433,9 +434,15 @@ public final class NativeStructurePostProcessor {
|
||||
return;
|
||||
}
|
||||
for (int y = originalY + 1; y < targetY; y++) {
|
||||
world.setBlock(position.set(x, y, z), materials.subsurface(), 2);
|
||||
position.set(x, y, z);
|
||||
if (!isTreeBlock(world.getBlockState(position))) {
|
||||
world.setBlock(position, materials.subsurface(), 2);
|
||||
}
|
||||
}
|
||||
position.set(x, targetY, z);
|
||||
if (!isTreeBlock(world.getBlockState(position))) {
|
||||
world.setBlock(position, materials.surface(), 2);
|
||||
}
|
||||
world.setBlock(position.set(x, targetY, z), materials.surface(), 2);
|
||||
}
|
||||
|
||||
private static BlockState clearSurfaceDecorationAndResolveFill(
|
||||
@@ -531,8 +538,10 @@ public final class NativeStructurePostProcessor {
|
||||
BlockState air = Blocks.AIR.defaultBlockState();
|
||||
for (StructureTemplate.StructureBlockInfo airBlock : airBlocks) {
|
||||
BlockPos airPosition = airBlock.pos();
|
||||
BlockState existingState = world.getBlockState(airPosition);
|
||||
if (shouldClearLegacyAir(
|
||||
airPosition.getY(), groundY, world.getBlockState(airPosition).isAir())) {
|
||||
airPosition.getY(), groundY, existingState.isAir())
|
||||
&& !isTreeBlock(existingState)) {
|
||||
world.setBlock(airPosition, air, 2);
|
||||
}
|
||||
}
|
||||
@@ -620,7 +629,7 @@ public final class NativeStructurePostProcessor {
|
||||
}
|
||||
boolean[] clearColumns = new boolean[area.getXSpan() * area.getZSpan()];
|
||||
for (VegetationTarget target : targets) {
|
||||
if (target != null && target.start() != null && target.start().isValid()) {
|
||||
if (target != null && target.force() && target.start() != null && target.start().isValid()) {
|
||||
markVegetationColumns(area, snapshot, target, clearColumns);
|
||||
}
|
||||
}
|
||||
@@ -673,25 +682,19 @@ public final class NativeStructurePostProcessor {
|
||||
private static void markVegetationColumns(BoundingBox area, VegetationSnapshot snapshot,
|
||||
VegetationTarget target, boolean[] clearColumns) {
|
||||
int width = area.getXSpan();
|
||||
int padding = requiresSurfaceTerrain(target.start()) ? SURFACE_TERRAIN_RADIUS : 0;
|
||||
int[] pieceTops = new int[clearColumns.length];
|
||||
Arrays.fill(pieceTops, Integer.MIN_VALUE);
|
||||
for (StructurePiece piece : target.start().getPieces()) {
|
||||
BoundingBox bounds = piece.getBoundingBox();
|
||||
int minX = Math.max(area.minX(), bounds.minX() - padding);
|
||||
int maxX = Math.min(area.maxX(), bounds.maxX() + padding);
|
||||
int minZ = Math.max(area.minZ(), bounds.minZ() - padding);
|
||||
int maxZ = Math.min(area.maxZ(), bounds.maxZ() + padding);
|
||||
int minX = Math.max(area.minX(), bounds.minX());
|
||||
int maxX = Math.min(area.maxX(), bounds.maxX());
|
||||
int minZ = Math.max(area.minZ(), bounds.minZ());
|
||||
int maxZ = Math.min(area.maxZ(), bounds.maxZ());
|
||||
if (minX > maxX || minZ > maxZ) {
|
||||
continue;
|
||||
}
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
if (padding > 0) {
|
||||
if (!withinSurfaceTerrainRadius(x, z, bounds, padding)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
int column = (z - area.minZ()) * width + x - area.minX();
|
||||
pieceTops[column] = Math.max(pieceTops[column], bounds.maxY());
|
||||
}
|
||||
@@ -707,12 +710,6 @@ public final class NativeStructurePostProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
static boolean withinSurfaceTerrainRadius(int x, int z, BoundingBox bounds, int radius) {
|
||||
int outX = IrisObjectVacuum.outset(x, bounds.minX(), bounds.maxX());
|
||||
int outZ = IrisObjectVacuum.outset(z, bounds.minZ(), bounds.maxZ());
|
||||
return (long) outX * outX + (long) outZ * outZ <= (long) radius * radius;
|
||||
}
|
||||
|
||||
static boolean shouldClearVegetationColumn(int pieceTopY, int lowestTreeY, boolean force) {
|
||||
return force || pieceTopY >= lowestTreeY;
|
||||
}
|
||||
@@ -742,7 +739,13 @@ public final class NativeStructurePostProcessor {
|
||||
}
|
||||
|
||||
private static boolean isTreeBlock(BlockState state) {
|
||||
return state.is(BlockTags.LOGS) || state.is(BlockTags.LEAVES);
|
||||
if (state.is(BlockTags.LOGS) || state.is(BlockTags.LEAVES)) {
|
||||
return true;
|
||||
}
|
||||
String path = BuiltInRegistries.BLOCK.getKey(state.getBlock()).getPath();
|
||||
return path.endsWith("_log") || path.endsWith("_wood")
|
||||
|| path.endsWith("_stem") || path.endsWith("_hyphae")
|
||||
|| path.endsWith("_leaves");
|
||||
}
|
||||
|
||||
private static List<FoundationColumn> foundationEnvelope(BoundingBox area, StructureStart start) {
|
||||
|
||||
+6
-6
@@ -242,10 +242,10 @@ final class IrisModdedBiomeSource extends BiomeSource {
|
||||
throw new IllegalStateException("Iris returned no surface structure biome at quart "
|
||||
+ quartX + "," + quartZ);
|
||||
}
|
||||
Holder<Biome> resolved = resolveHolder(registry, irisBiome.getVanillaDerivativeKey());
|
||||
Holder<Biome> resolved = resolveHolder(registry, irisBiome.getStructureDerivativeKey());
|
||||
if (resolved == null) {
|
||||
throw new IllegalStateException("Iris structure biome derivative '"
|
||||
+ irisBiome.getVanillaDerivativeKey() + "' is not registered at quart "
|
||||
+ irisBiome.getStructureDerivativeKey() + "' is not registered at quart "
|
||||
+ quartX + "," + quartZ);
|
||||
}
|
||||
return resolved;
|
||||
@@ -262,10 +262,10 @@ final class IrisModdedBiomeSource extends BiomeSource {
|
||||
if (registry == null) {
|
||||
throw new IllegalStateException("Iris structure biome lookup has no biome registry");
|
||||
}
|
||||
Holder<Biome> resolved = resolveHolder(registry, resolution.irisBiome().getVanillaDerivativeKey());
|
||||
Holder<Biome> resolved = resolveHolder(registry, resolution.irisBiome().getStructureDerivativeKey());
|
||||
if (resolved == null) {
|
||||
throw new IllegalStateException("Iris structure biome derivative '"
|
||||
+ resolution.irisBiome().getVanillaDerivativeKey() + "' is not registered at block "
|
||||
+ resolution.irisBiome().getStructureDerivativeKey() + "' is not registered at block "
|
||||
+ resolution.blockX() + "," + resolution.blockY() + "," + resolution.blockZ());
|
||||
}
|
||||
return resolved;
|
||||
@@ -353,7 +353,7 @@ final class IrisModdedBiomeSource extends BiomeSource {
|
||||
throw new IllegalStateException("Iris returned no structure biome at quart "
|
||||
+ quartX + "," + quartY + "," + quartZ);
|
||||
}
|
||||
String derivativeKey = irisBiome.getVanillaDerivativeKey();
|
||||
String derivativeKey = irisBiome.getStructureDerivativeKey();
|
||||
Holder<Biome> resolved = resolveHolder(registry, derivativeKey);
|
||||
if (resolved == null) {
|
||||
throw new IllegalStateException("Iris structure biome derivative '" + derivativeKey
|
||||
@@ -435,7 +435,7 @@ final class IrisModdedBiomeSource extends BiomeSource {
|
||||
LinkedHashSet<String> possible = new LinkedHashSet<>();
|
||||
String namespace = engine.getDimension().getLoadKey().toLowerCase(Locale.ROOT);
|
||||
for (IrisBiome irisBiome : engine.getAllBiomes()) {
|
||||
String derivative = normalizeKey(irisBiome.getVanillaDerivativeKey());
|
||||
String derivative = normalizeKey(irisBiome.getStructureDerivativeKey());
|
||||
if (derivative != null) {
|
||||
possible.add(derivative);
|
||||
}
|
||||
|
||||
+16
-2
@@ -28,6 +28,7 @@ import art.arcane.iris.engine.object.IrisBiome;
|
||||
import art.arcane.iris.engine.object.IrisBiomeCustom;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.engine.object.IrisNativeStructureDecision;
|
||||
import art.arcane.iris.engine.object.IrisRegion;
|
||||
import art.arcane.iris.engine.object.IrisStructureStiltSettings;
|
||||
import art.arcane.iris.engine.object.NativeStructureGenerationStatus;
|
||||
import art.arcane.iris.nativegen.NativeStructureGenerationException;
|
||||
@@ -611,7 +612,20 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
|
||||
}
|
||||
|
||||
static Set<String> collectConfiguredBiomeKeys(IrisDimension dimension, IrisData data) {
|
||||
return collectConfiguredBiomeKeys(dimension.getReachableBiomes(() -> data), dimension.getLoadKey());
|
||||
LinkedHashSet<String> keys = new LinkedHashSet<>(
|
||||
collectConfiguredBiomeKeys(dimension.getReachableBiomes(() -> data), dimension.getLoadKey()));
|
||||
for (IrisRegion region : dimension.getAllRegions(() -> data)) {
|
||||
if (region == null) {
|
||||
continue;
|
||||
}
|
||||
if (!region.getSeaBiomes().isEmpty()) {
|
||||
keys.add("minecraft:the_void");
|
||||
}
|
||||
if (!region.getShoreBiomes().isEmpty()) {
|
||||
keys.add("minecraft:beach");
|
||||
}
|
||||
}
|
||||
return Set.copyOf(keys);
|
||||
}
|
||||
|
||||
static Set<String> collectConfiguredBiomeKeys(Iterable<IrisBiome> biomes, String dimensionLoadKey) {
|
||||
@@ -621,7 +635,7 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
|
||||
if (irisBiome == null) {
|
||||
continue;
|
||||
}
|
||||
Identifier derivative = Identifier.tryParse(irisBiome.getVanillaDerivativeKey());
|
||||
Identifier derivative = Identifier.tryParse(irisBiome.getStructureDerivativeKey());
|
||||
if (derivative != null) {
|
||||
keys.add(derivative.toString().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
+10
-2
@@ -4,6 +4,7 @@ import art.arcane.iris.engine.object.IrisBiome;
|
||||
import art.arcane.iris.engine.object.IrisBiomeCustom;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.engine.object.IrisRange;
|
||||
import art.arcane.iris.engine.object.InferredType;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.mantle.flag.MantleFlag;
|
||||
import net.minecraft.SharedConstants;
|
||||
@@ -101,11 +102,18 @@ public class IrisModdedStructureParityTest {
|
||||
IrisBiome custom = new IrisBiome()
|
||||
.setDerivative("forest")
|
||||
.setCustomDerivitives(new KList<>(new IrisBiomeCustom().setId("Aurora")));
|
||||
IrisBiome shore = new IrisBiome()
|
||||
.setVanillaDerivative("minecraft:desert")
|
||||
.setInferredType(InferredType.SHORE);
|
||||
IrisBiome unsafeSea = new IrisBiome()
|
||||
.setVanillaDerivative("minecraft:plains")
|
||||
.setInferredType(InferredType.SEA);
|
||||
|
||||
Set<String> keys = IrisModdedChunkGenerator.collectConfiguredBiomeKeys(
|
||||
List.of(ocean, custom), "OverWorld");
|
||||
List.of(ocean, custom, shore, unsafeSea), "OverWorld");
|
||||
|
||||
assertEquals(Set.of("minecraft:deep_ocean", "minecraft:forest", "overworld:aurora"), keys);
|
||||
assertEquals(Set.of("minecraft:deep_ocean", "minecraft:forest", "minecraft:beach",
|
||||
"minecraft:the_void", "overworld:aurora"), keys);
|
||||
assertFalse(keys.contains("minecraft:desert"));
|
||||
assertFalse(keys.contains("minecraft:plains"));
|
||||
}
|
||||
|
||||
@@ -113,6 +113,8 @@ nmsBindings.each { key, value ->
|
||||
rootProject.file("adapters/bukkit/nms/${key}/src/main/java/art/arcane/iris/core/nms/${key}/IrisChunkGenerator.java").absolutePath)
|
||||
systemProperty('iris.nativeStructurePostProcessorSource',
|
||||
rootProject.file('adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructurePostProcessor.java').absolutePath)
|
||||
systemProperty('iris.customBiomeSource',
|
||||
rootProject.file("adapters/bukkit/nms/${key}/src/main/java/art/arcane/iris/core/nms/${key}/CustomBiomeSource.java").absolutePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Servers
|
||||
* Copyright (c) 2026 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.core.structure;
|
||||
|
||||
public final class NativeStructureLocateCapability {
|
||||
private static final String MONUMENT_KEY = "minecraft:monument";
|
||||
private static final String UNAVAILABLE_MESSAGE = "Native monument generation is enabled, but synchronous monument locating is unavailable because a cold search can stall the server thread. The locate request was not run.";
|
||||
|
||||
private NativeStructureLocateCapability() {
|
||||
}
|
||||
|
||||
public static boolean isPaperUnavailable(String structureKey) {
|
||||
return structureKey != null && MONUMENT_KEY.equalsIgnoreCase(structureKey.trim());
|
||||
}
|
||||
|
||||
public static String unavailableMessage() {
|
||||
return UNAVAILABLE_MESSAGE;
|
||||
}
|
||||
}
|
||||
+27
-24
@@ -41,6 +41,8 @@ import art.arcane.iris.engine.object.IrisStructurePlacement;
|
||||
import art.arcane.iris.engine.object.IrisStructureStiltSettings;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.spi.PlatformBlockState;
|
||||
import art.arcane.iris.util.common.data.B;
|
||||
import art.arcane.iris.util.common.math.IrisBlockVector;
|
||||
import art.arcane.iris.util.project.matter.TileWrapper;
|
||||
import art.arcane.iris.util.project.noise.CNG;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
@@ -67,7 +69,6 @@ public class IrisStructureComponent extends IrisMantleComponent {
|
||||
private static final double OVERBORE_MIN_BOUNDARY_SQUARED = OVERBORE_MIN_BOUNDARY * OVERBORE_MIN_BOUNDARY;
|
||||
private static final double OVERBORE_BOUNDARY_SPAN = 0.8;
|
||||
private static final double OVERBORE_MAX_UP_REACH = 1.8;
|
||||
private static final int DYNAMIC_STRUCTURE_Y_TOLERANCE = 32;
|
||||
private static final MatterCavern CARVE_CAVERN = new MatterCavern(true, "", (byte) 3);
|
||||
|
||||
public IrisStructureComponent(EngineMantle engineMantle) {
|
||||
@@ -409,18 +410,19 @@ public class IrisStructureComponent extends IrisMantleComponent {
|
||||
private void clearIntersectingObjectTrees(MantleWriter writer, IrisStructureLocator.ResolvedPlacement resolved) {
|
||||
int mantleOffset = getEngineMantle().getEngine().getMinHeight();
|
||||
int worldHeight = getEngineMantle().getEngine().getHeight();
|
||||
int verticalTolerance = resolved.exactY() ? 0 : DYNAMIC_STRUCTURE_Y_TOLERANCE;
|
||||
Map<String, ObjectMarkerBounds> intersectingObjects = new HashMap<>();
|
||||
for (PlacedStructurePiece piece : resolved.pieces()) {
|
||||
int minY = Math.max(0, piece.getMinY() - mantleOffset - verticalTolerance);
|
||||
int maxY = Math.min(worldHeight - 1, piece.getMaxY() - mantleOffset + verticalTolerance);
|
||||
if (minY > maxY) {
|
||||
continue;
|
||||
}
|
||||
for (int x = piece.getMinX(); x <= piece.getMaxX(); x++) {
|
||||
for (int z = piece.getMinZ(); z <= piece.getMaxZ(); z++) {
|
||||
collectIntersectingObjectTreeMarkers(
|
||||
writer, x, z, minY, maxY, intersectingObjects);
|
||||
for (IrisBlockVector local : piece.getObject().getBlocks().keys()) {
|
||||
PlatformBlockState structureState = piece.getObject().getBlocks().get(local);
|
||||
if (!isOccupiedStructureState(structureState)) {
|
||||
continue;
|
||||
}
|
||||
IrisBlockVector rotated = piece.getRotation().rotate(local.clone());
|
||||
int y = piece.getY() - mantleOffset + rotated.getBlockY();
|
||||
if (y >= 0 && y < worldHeight) {
|
||||
collectIntersectingObjectTreeMarker(
|
||||
writer, piece.getX() + rotated.getBlockX(), y,
|
||||
piece.getZ() + rotated.getBlockZ(), intersectingObjects);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -429,19 +431,20 @@ public class IrisStructureComponent extends IrisMantleComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private void collectIntersectingObjectTreeMarkers(MantleWriter writer, int x, int z,
|
||||
int minY, int maxY,
|
||||
Map<String, ObjectMarkerBounds> intersectingObjects) {
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
PlatformBlockState state = writer.getDataIfPresent(x, y, z, PlatformBlockState.class);
|
||||
if (state == null || !state.isTreeBlock()) {
|
||||
continue;
|
||||
}
|
||||
String marker = writer.getDataIfPresent(x, y, z, String.class);
|
||||
if (isOrdinaryObjectMarker(marker)) {
|
||||
intersectingObjects.computeIfAbsent(marker, ignored -> new ObjectMarkerBounds())
|
||||
.include(x, y, z);
|
||||
}
|
||||
static boolean isOccupiedStructureState(PlatformBlockState state) {
|
||||
return !B.isAir(state);
|
||||
}
|
||||
|
||||
private void collectIntersectingObjectTreeMarker(MantleWriter writer, int x, int y, int z,
|
||||
Map<String, ObjectMarkerBounds> intersectingObjects) {
|
||||
PlatformBlockState state = writer.getDataIfPresent(x, y, z, PlatformBlockState.class);
|
||||
if (state == null || !state.isTreeBlock()) {
|
||||
return;
|
||||
}
|
||||
String marker = writer.getDataIfPresent(x, y, z, String.class);
|
||||
if (isOrdinaryObjectMarker(marker)) {
|
||||
intersectingObjects.computeIfAbsent(marker, ignored -> new ObjectMarkerBounds())
|
||||
.include(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
||||
@Desc("The raw derivative of this biome. This is required or the terrain will not properly generate. Use any vanilla biome type. Look in examples/biome-list.txt")
|
||||
private String derivative = "minecraft:the_void";
|
||||
@Required
|
||||
@Desc("Override the derivative when vanilla places structures to this derivative. This is useful for example if you have an ocean biome, but you have set the derivative to desert to get a brown-ish color. To prevent desert structures from spawning on top of your ocean, you can set your vanillaDerivative to ocean, to allow for vanilla structures. Not defining this value will simply select the derivative.")
|
||||
@Desc("Override the derivative used for vanilla structure selection. Iris still enforces the generated terrain role: land-only Minecraft derivatives on sea biomes expose no native structure biome, and land-only derivatives on shore biomes resolve as beach, while exact ocean, river, beach, and shore variants remain eligible. Non-Minecraft namespaces remain authoritative. Not defining this value selects derivative.")
|
||||
private String vanillaDerivative = null;
|
||||
@ArrayType(min = 1, type = String.class)
|
||||
@Desc("You can instead specify multiple biome derivatives to randomly scatter colors in this biome")
|
||||
@@ -315,6 +315,20 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
||||
return resolved == null ? namespacedBiomeKey(derivative) : resolved;
|
||||
}
|
||||
|
||||
public String getStructureDerivativeKey() {
|
||||
String key = getVanillaDerivativeKey();
|
||||
if (key == null || !key.startsWith("minecraft:")) {
|
||||
return key;
|
||||
}
|
||||
if (isSea() && !isVanillaSeaStructureBiome(key)) {
|
||||
return "minecraft:the_void";
|
||||
}
|
||||
if (isShore() && !isVanillaShoreStructureBiome(key)) {
|
||||
return "minecraft:beach";
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
public String getDerivativeKey() {
|
||||
return namespacedBiomeKey(derivative);
|
||||
}
|
||||
@@ -327,6 +341,14 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
||||
return trimmed.indexOf(':') >= 0 ? trimmed : "minecraft:" + trimmed;
|
||||
}
|
||||
|
||||
static boolean isVanillaSeaStructureBiome(String key) {
|
||||
return key != null && (key.contains("ocean") || key.endsWith("river"));
|
||||
}
|
||||
|
||||
static boolean isVanillaShoreStructureBiome(String key) {
|
||||
return key != null && (key.endsWith("beach") || key.endsWith("shore"));
|
||||
}
|
||||
|
||||
private KList<Biome> getBiomeScatterResolved() {
|
||||
return biomeScatterResolved.aquire(() -> resolveBiomeKeys(biomeScatter));
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public class IrisImportedStructureControl {
|
||||
private boolean datapackOverrides = true;
|
||||
|
||||
@ArrayType(type = IrisVanillaStructureAdjustment.class, min = 1)
|
||||
@Desc("Per-structure adjustments applied to vanilla, mod, and datapack structures that still generate natively. Vertical shifts from every matching entry stack. Surface-intersecting structures clear logs and leaves automatically; a matching vegetation option can force clearing for unusual placements. The last matching entry with stilt settings controls foundation columns. A structure suppressed by an Iris placement is unaffected.")
|
||||
@Desc("Per-structure adjustments applied to vanilla, mod, and datapack structures that still generate natively. Vertical shifts from every matching entry stack. A matching vegetation option explicitly clears logs and leaves inside structure piece bounds. The last matching entry with stilt settings controls foundation columns. A structure suppressed by an Iris placement is unaffected.")
|
||||
private KList<IrisVanillaStructureAdjustment> adjustments = new KList<>();
|
||||
|
||||
public boolean shouldGenerate(String key) {
|
||||
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Servers
|
||||
* Copyright (c) 2026 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.core.structure;
|
||||
|
||||
import art.arcane.iris.engine.object.IrisImportedStructureControl;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class NativeStructureLocateCapabilityTest {
|
||||
@Test
|
||||
public void monumentLocateIsUnavailableWithoutChangingGenerationPolicy() {
|
||||
assertTrue(NativeStructureLocateCapability.isPaperUnavailable("minecraft:monument"));
|
||||
assertTrue(NativeStructureLocateCapability.isPaperUnavailable(" MINECRAFT:MONUMENT "));
|
||||
assertTrue(new IrisImportedStructureControl().shouldGenerate("minecraft:monument"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void otherStructuresRemainLocatable() {
|
||||
assertFalse(NativeStructureLocateCapability.isPaperUnavailable(null));
|
||||
assertFalse(NativeStructureLocateCapability.isPaperUnavailable(""));
|
||||
assertFalse(NativeStructureLocateCapability.isPaperUnavailable("minecraft:stronghold"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void messageStatesThatGenerationRemainsEnabled() {
|
||||
String message = NativeStructureLocateCapability.unavailableMessage().toLowerCase();
|
||||
assertTrue(message.contains("generation is enabled"));
|
||||
assertTrue(message.contains("locat"));
|
||||
}
|
||||
}
|
||||
+12
@@ -41,6 +41,18 @@ public class IrisStructureComponentMarkerTest {
|
||||
assertNotEquals(first, moved);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void treeCollisionUsesOnlyNonAirStructureBlocks() {
|
||||
PlatformBlockState solid = mock(PlatformBlockState.class);
|
||||
PlatformBlockState air = mock(PlatformBlockState.class);
|
||||
when(solid.isAir()).thenReturn(false);
|
||||
when(air.isAir()).thenReturn(true);
|
||||
|
||||
assertTrue(IrisStructureComponent.isOccupiedStructureState(solid));
|
||||
assertFalse(IrisStructureComponent.isOccupiedStructureState(air));
|
||||
assertFalse(IrisStructureComponent.isOccupiedStructureState(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyResolvedPiecesRemainSkippableForNonReplacement() {
|
||||
IrisStructureLocator.ResolvedPlacement resolved = resolvedPlacement(
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class IrisBiomeStructureDerivativeTest {
|
||||
@Test
|
||||
public void seaBiomeRejectsLandOnlyVanillaDerivativeForStructures() {
|
||||
IrisBiome biome = new IrisBiome()
|
||||
.setDerivative("minecraft:warm_ocean")
|
||||
.setVanillaDerivative("minecraft:desert")
|
||||
.setInferredType(InferredType.SEA);
|
||||
|
||||
assertEquals("minecraft:desert", biome.getVanillaDerivativeKey());
|
||||
assertEquals("minecraft:the_void", biome.getStructureDerivativeKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shoreBiomeRejectsLandOnlyVanillaDerivativeForStructures() {
|
||||
IrisBiome biome = new IrisBiome()
|
||||
.setDerivative("minecraft:warm_ocean")
|
||||
.setVanillaDerivative("minecraft:savanna")
|
||||
.setInferredType(InferredType.SHORE);
|
||||
|
||||
assertEquals("minecraft:beach", biome.getStructureDerivativeKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void waterStructureDerivativesKeepTheirExactVariant() {
|
||||
IrisBiome deepOcean = new IrisBiome()
|
||||
.setVanillaDerivative("minecraft:deep_cold_ocean")
|
||||
.setInferredType(InferredType.SEA);
|
||||
IrisBiome snowyBeach = new IrisBiome()
|
||||
.setVanillaDerivative("minecraft:snowy_beach")
|
||||
.setInferredType(InferredType.SHORE);
|
||||
|
||||
assertEquals("minecraft:deep_cold_ocean", deepOcean.getStructureDerivativeKey());
|
||||
assertEquals("minecraft:snowy_beach", snowyBeach.getStructureDerivativeKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void seaAndShoreDoNotBorrowEachOthersStructureBiomes() {
|
||||
IrisBiome seaBeach = new IrisBiome()
|
||||
.setVanillaDerivative("minecraft:beach")
|
||||
.setInferredType(InferredType.SEA);
|
||||
IrisBiome shoreOcean = new IrisBiome()
|
||||
.setVanillaDerivative("minecraft:ocean")
|
||||
.setInferredType(InferredType.SHORE);
|
||||
|
||||
assertEquals("minecraft:the_void", seaBeach.getStructureDerivativeKey());
|
||||
assertEquals("minecraft:beach", shoreOcean.getStructureDerivativeKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void landAndModdedDerivativesRemainAuthoritative() {
|
||||
IrisBiome land = new IrisBiome()
|
||||
.setVanillaDerivative("minecraft:desert")
|
||||
.setInferredType(InferredType.LAND);
|
||||
IrisBiome moddedSea = new IrisBiome()
|
||||
.setVanillaDerivative("example:abyss")
|
||||
.setInferredType(InferredType.SEA);
|
||||
|
||||
assertEquals("minecraft:desert", land.getStructureDerivativeKey());
|
||||
assertEquals("example:abyss", moddedSea.getStructureDerivativeKey());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user