mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-27 04:37:47 +00:00
Temp vag
This commit is contained in:
+1
-1
@@ -606,7 +606,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
world, area, terrainTargets, this::resolvePaletteBlock);
|
||||
} catch (Throwable error) {
|
||||
throw NativeStructureGenerationException.failure(
|
||||
"terrain carving", nativeStructureBatchContext(placementGroups),
|
||||
"terrain preparation", nativeStructureBatchContext(placementGroups),
|
||||
chunkPos.x(), chunkPos.z(), error);
|
||||
}
|
||||
for (NativePlacementGroup group : placementGroups) {
|
||||
|
||||
+2
@@ -44,6 +44,8 @@ public class IrisChunkGeneratorFailureContractTest {
|
||||
assertFalse(adjustment.contains("IrisLogging.reportError"));
|
||||
assertTrue(placement.contains("\"resolution\""));
|
||||
assertTrue(placement.contains("\"terrain integration\""));
|
||||
assertTrue(placement.contains("\"terrain preparation\""));
|
||||
assertFalse(placement.contains("\"terrain carving\""));
|
||||
assertTrue(placement.contains("\"vegetation cleanup\""));
|
||||
assertTrue(placement.contains("\"placement\""));
|
||||
assertTrue(placement.contains("because structure generation is disabled outside the pack"));
|
||||
|
||||
+130
-1
@@ -16,6 +16,7 @@ import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.levelgen.LegacyRandomSource;
|
||||
import net.minecraft.world.level.levelgen.RandomSupport;
|
||||
import net.minecraft.world.level.levelgen.WorldgenRandom;
|
||||
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||
import net.minecraft.world.level.levelgen.structure.StructurePiece;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.pieces.PiecesContainer;
|
||||
@@ -31,6 +32,7 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class NativeStructureOwnershipRecoveryTest {
|
||||
@@ -138,6 +140,110 @@ public class NativeStructureOwnershipRecoveryTest {
|
||||
assertFalse(NativeStructureOwnershipFingerprint.matches(recovered, moved));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staleVacuumEnvelopeRefreshesWithoutReplacingOwnershipIdentity() {
|
||||
String structureKey = "minecraft:monument";
|
||||
long seed = 648231L;
|
||||
ChunkPos origin = new ChunkPos(5, -6);
|
||||
OceanMonumentStructure structure = structure();
|
||||
StructureStart start = monumentStart(structure, origin, seed);
|
||||
NativeStructureStartPlan plan = plan(
|
||||
origin, "vacuum-envelope-refresh", IrisStructureTerrainMode.VACUUM, 0);
|
||||
BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(start);
|
||||
NativeStructureOwnershipRecord stale = NativeStructureOwnershipFingerprint.capture(
|
||||
structureKey, start, plan, content);
|
||||
|
||||
NativeStructureOwnershipRecord refreshed =
|
||||
NativeStructureOwnershipRecovery.refreshReferenceEnvelope(
|
||||
structureKey, structure, start, stale);
|
||||
|
||||
assertNotNull(refreshed);
|
||||
assertNotEquals(stale, refreshed);
|
||||
assertEquals(stale.schema(), refreshed.schema());
|
||||
assertEquals(stale.ownershipKey(), refreshed.ownershipKey());
|
||||
assertEquals(stale.placementIdentity(), refreshed.placementIdentity());
|
||||
assertEquals(stale.baseY(), refreshed.baseY());
|
||||
assertEquals(stale.locatorY(), refreshed.locatorY());
|
||||
assertEquals(stale.contentFingerprint(), refreshed.contentFingerprint());
|
||||
assertEquals(stale.decision(), refreshed.decision());
|
||||
assertEquals(IrisStructureTerrainMode.VACUUM,
|
||||
refreshed.restoredDecision().terrain().resolvedMode());
|
||||
BoundingBox expected = NativeStructureReferenceEnvelope.referenceBounds(
|
||||
start, structure, plan.placement().resolvedTerrain(), structureKey);
|
||||
assertEquals(expected.minX() >> 4, refreshed.referenceMinChunkX());
|
||||
assertEquals(expected.maxX() >> 4, refreshed.referenceMaxChunkX());
|
||||
assertEquals(expected.minZ() >> 4, refreshed.referenceMinChunkZ());
|
||||
assertEquals(expected.maxZ() >> 4, refreshed.referenceMaxChunkZ());
|
||||
assertTrue(hasExpandedCoverage(stale, refreshed));
|
||||
assertSame(refreshed, NativeStructureOwnershipRecovery.refreshReferenceEnvelope(
|
||||
structureKey, structure, start, refreshed));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staleEnvelopeCannotRefreshAgainstDifferentContent() {
|
||||
String structureKey = "minecraft:monument";
|
||||
long seed = 412987L;
|
||||
ChunkPos origin = new ChunkPos(-2, 7);
|
||||
OceanMonumentStructure structure = structure();
|
||||
StructureStart expected = monumentStart(structure, origin, seed);
|
||||
NativeStructureStartPlan plan = plan(
|
||||
origin, "vacuum-content-check", IrisStructureTerrainMode.VACUUM, 0);
|
||||
NativeStructureOwnershipRecord stale = NativeStructureOwnershipFingerprint.capture(
|
||||
structureKey, expected, plan,
|
||||
NativeStructureReferenceEnvelope.contentBounds(expected));
|
||||
StructureStart moved = monumentStart(structure, origin, seed);
|
||||
for (StructurePiece piece : moved.getPieces()) {
|
||||
piece.move(1, 0, 0);
|
||||
}
|
||||
|
||||
assertNull(NativeStructureOwnershipRecovery.refreshReferenceEnvelope(
|
||||
structureKey, structure, moved, stale));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void currentNonVacuumEnvelopeRemainsThePersistedAuthority() {
|
||||
String structureKey = "minecraft:monument";
|
||||
long seed = 927451L;
|
||||
ChunkPos origin = new ChunkPos(3, 8);
|
||||
OceanMonumentStructure structure = structure();
|
||||
StructureStart start = monumentStart(structure, origin, seed);
|
||||
NativeStructureStartPlan plan = plan(origin, "current-force-carve", 24);
|
||||
BoundingBox envelope = NativeStructureReferenceEnvelope.referenceBounds(
|
||||
start, structure, plan.placement().resolvedTerrain(), structureKey);
|
||||
NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture(
|
||||
structureKey, start, plan, envelope);
|
||||
|
||||
assertSame(ownership, NativeStructureOwnershipRecovery.refreshReferenceEnvelope(
|
||||
structureKey, structure, start, ownership));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clippedVacuumEnvelopeRemainsStableAtTheReferenceLimit() {
|
||||
String structureKey = "minecraft:monument";
|
||||
long seed = 381729L;
|
||||
ChunkPos origin = new ChunkPos(0, 0);
|
||||
OceanMonumentStructure structure = structure();
|
||||
StructureStart start = monumentStart(structure, origin, seed);
|
||||
BoundingBox initial = NativeStructureReferenceEnvelope.contentBounds(start);
|
||||
int maximumReferenceBlockX = ((origin.x()
|
||||
+ NativeStructureOwnershipRecord.MAX_REFERENCE_DISTANCE_CHUNKS) << 4) + 15;
|
||||
int shiftX = maximumReferenceBlockX - initial.maxX();
|
||||
for (StructurePiece piece : start.getPieces()) {
|
||||
piece.move(shiftX, 0, 0);
|
||||
}
|
||||
NativeStructureStartPlan plan = plan(
|
||||
origin, "clipped-vacuum", IrisStructureTerrainMode.VACUUM, 0);
|
||||
BoundingBox envelope = NativeStructureReferenceEnvelope.referenceBounds(
|
||||
start, structure, plan.placement().resolvedTerrain(), structureKey);
|
||||
NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture(
|
||||
structureKey, start, plan, envelope);
|
||||
|
||||
assertEquals(origin.x() + NativeStructureOwnershipRecord.MAX_REFERENCE_DISTANCE_CHUNKS,
|
||||
ownership.referenceMaxChunkX());
|
||||
assertSame(ownership, NativeStructureOwnershipRecovery.refreshReferenceEnvelope(
|
||||
structureKey, structure, start, ownership));
|
||||
}
|
||||
|
||||
private static OceanMonumentStructure structure() {
|
||||
return new OceanMonumentStructure(
|
||||
new OceanMonumentStructure.StructureSettings(HolderSet.empty()));
|
||||
@@ -163,11 +269,19 @@ public class NativeStructureOwnershipRecoveryTest {
|
||||
private static NativeStructureStartPlan plan(ChunkPos origin,
|
||||
String placementId,
|
||||
int horizontalPadding) {
|
||||
return plan(origin, placementId,
|
||||
IrisStructureTerrainMode.FORCE_CARVE, horizontalPadding);
|
||||
}
|
||||
|
||||
private static NativeStructureStartPlan plan(ChunkPos origin,
|
||||
String placementId,
|
||||
IrisStructureTerrainMode terrainMode,
|
||||
int horizontalPadding) {
|
||||
IrisNativeStructure source = new IrisNativeStructure()
|
||||
.setStructure("minecraft:monument")
|
||||
.setWeight(1);
|
||||
IrisStructureTerrain terrain = new IrisStructureTerrain()
|
||||
.setMode(IrisStructureTerrainMode.FORCE_CARVE)
|
||||
.setMode(terrainMode)
|
||||
.setHorizontalPadding(horizontalPadding);
|
||||
IrisStructurePlacement placement = new IrisStructurePlacement()
|
||||
.setPlacementId(placementId)
|
||||
@@ -182,4 +296,19 @@ public class NativeStructureOwnershipRecoveryTest {
|
||||
monumentStart(structure(), origin, 1L)).minY()
|
||||
);
|
||||
}
|
||||
|
||||
private static boolean hasExpandedCoverage(
|
||||
NativeStructureOwnershipRecord stale,
|
||||
NativeStructureOwnershipRecord refreshed) {
|
||||
for (int chunkX = refreshed.referenceMinChunkX();
|
||||
chunkX <= refreshed.referenceMaxChunkX(); chunkX++) {
|
||||
for (int chunkZ = refreshed.referenceMinChunkZ();
|
||||
chunkZ <= refreshed.referenceMaxChunkZ(); chunkZ++) {
|
||||
if (!stale.covers(chunkX, chunkZ)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+582
-12
@@ -6,14 +6,18 @@ import art.arcane.iris.engine.object.IrisStructureTerrain;
|
||||
import art.arcane.iris.engine.object.IrisStructureTerrainMode;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.core.IdMapper;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
@@ -36,13 +40,18 @@ import net.minecraft.world.level.levelgen.structure.StructurePiece;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.TerrainAdjustment;
|
||||
import net.minecraft.world.level.levelgen.structure.pieces.PiecesContainer;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.LegacySinglePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.ListPoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.LiquidSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.BlockIgnoreProcessor;
|
||||
import net.minecraft.world.level.levelgen.structure.structures.DesertPyramidPiece;
|
||||
import net.minecraft.world.level.levelgen.structure.structures.DesertPyramidStructure;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.junit.BeforeClass;
|
||||
@@ -55,8 +64,11 @@ import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -118,6 +130,34 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
new IrisStructureTerrain().setMode(IrisStructureTerrainMode.PRESERVE))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitVacuumForcesThinSurfaceFittingWithoutAuthoredAdaptation() {
|
||||
StructureStart none = desertStart(TerrainAdjustment.NONE);
|
||||
StructureStart box = desertStart(TerrainAdjustment.BEARD_BOX);
|
||||
NativeStructureTerrainIntegrator.TerrainTarget sourceNone =
|
||||
new NativeStructureTerrainIntegrator.TerrainTarget(
|
||||
"test:none", none,
|
||||
new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE));
|
||||
NativeStructureTerrainIntegrator.TerrainTarget vacuumNone =
|
||||
new NativeStructureTerrainIntegrator.TerrainTarget(
|
||||
"test:none", none,
|
||||
new IrisStructureTerrain().setMode(IrisStructureTerrainMode.VACUUM));
|
||||
NativeStructureTerrainIntegrator.TerrainTarget vacuumBox =
|
||||
new NativeStructureTerrainIntegrator.TerrainTarget(
|
||||
"test:box", box,
|
||||
new IrisStructureTerrain().setMode(IrisStructureTerrainMode.VACUUM));
|
||||
|
||||
assertFalse(NativeStructureSurfaceFitter.requiresSurfaceTerrain(sourceNone));
|
||||
assertTrue(NativeStructureSurfaceFitter.requiresSurfaceTerrain(vacuumNone));
|
||||
assertTrue(NativeStructureSurfaceFitter.requiresSurfaceTerrain(vacuumBox));
|
||||
assertEquals(TerrainAdjustment.BEARD_THIN,
|
||||
NativeStructureSurfaceFitter.effectiveSurfaceAdjustment(vacuumNone));
|
||||
assertEquals(TerrainAdjustment.BEARD_THIN,
|
||||
NativeStructureSurfaceFitter.effectiveSurfaceAdjustment(vacuumBox));
|
||||
assertTrue(NativeStructureTerrainIntegrator.clearsLegacyTemplateAir(
|
||||
none, vacuumNone.terrain()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beardBoxUsesTheFullRigidHeightWhileBeardThinUsesTheGroundPlane() {
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor thin =
|
||||
@@ -210,11 +250,12 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
}
|
||||
}
|
||||
|
||||
NativeStructureSurfaceFitter.prepareSurfaceStructures(
|
||||
world(blocks), area,
|
||||
List.of(new NativeStructureTerrainIntegrator.TerrainTarget(
|
||||
List<NativeStructureTerrainIntegrator.TerrainTarget> targets = List.of(
|
||||
new NativeStructureTerrainIntegrator.TerrainTarget(
|
||||
"nova_structures:tavern_oak", start,
|
||||
new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE))),
|
||||
new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE)));
|
||||
NativeStructureSurfaceFitter.prepareSurfaceStructures(
|
||||
world(blocks), area, targets,
|
||||
(x, z) -> 64);
|
||||
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 1, 71, 1));
|
||||
@@ -263,6 +304,392 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
List.of(strongTie, weakTie), 2, 2, 64));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postClearSupportUsesUpperOccupancyOverLowerLegacyAir() throws Exception {
|
||||
StructureTemplate lowerTemplate = template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState()),
|
||||
block(1, 0, 0, Blocks.AIR.defaultBlockState())));
|
||||
StructureTemplate upperTemplate = template(List.of(
|
||||
block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState()),
|
||||
block(1, 0, 0, Blocks.COBBLESTONE.defaultBlockState())));
|
||||
PoolElementStructurePiece lower = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(lowerTemplate),
|
||||
new BoundingBox(0, 62, 0, 1, 65, 0), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece upper = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(upperTemplate),
|
||||
new BoundingBox(0, 64, 0, 1, 70, 0), 1, Rotation.NONE);
|
||||
StructureStart start = rigidSurfaceStart(List.of(lower, upper));
|
||||
BoundingBox area = new BoundingBox(0, 58, 0, 1, 72, 0);
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
for (int x = 0; x <= 1; x++) {
|
||||
put(blocks, x, 61, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, x, 62, 0, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
}
|
||||
put(blocks, 1, 63, 0, Blocks.DIRT.defaultBlockState());
|
||||
|
||||
Set<Long> written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(blocks), area, List.of(surfaceTarget(start)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager);
|
||||
|
||||
assertEquals(Set.of(BlockPos.asLong(0, 63, 0)), written);
|
||||
assertEquals(Blocks.DIRT.defaultBlockState(), state(blocks, 0, 63, 0));
|
||||
assertEquals(Blocks.DIRT.defaultBlockState(), state(blocks, 1, 63, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rotatedLowestAuthoredVoidFluidAirAndJigsawColumnsRemainOpen() throws Exception {
|
||||
List<StructureTemplate.StructureBlockInfo> upperBlocks = new ArrayList<>();
|
||||
upperBlocks.add(block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState()));
|
||||
upperBlocks.add(block(1, 0, 0, Blocks.AIR.defaultBlockState()));
|
||||
upperBlocks.add(block(2, 0, 0, Blocks.STRUCTURE_VOID.defaultBlockState()));
|
||||
upperBlocks.add(block(3, 0, 0, Blocks.WATER.defaultBlockState()));
|
||||
upperBlocks.add(block(4, 0, 0, Blocks.JIGSAW.defaultBlockState()));
|
||||
for (int x = 1; x <= 4; x++) {
|
||||
upperBlocks.add(block(x, 1, 0, Blocks.STONE.defaultBlockState()));
|
||||
}
|
||||
StructureTemplate upperTemplate = template(upperBlocks);
|
||||
PoolElementStructurePiece lower = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState())))),
|
||||
new BoundingBox(10, 62, 10, 10, 65, 14), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece upper = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(upperTemplate),
|
||||
new BoundingBox(10, 64, 10, 10, 70, 14), 1,
|
||||
Rotation.CLOCKWISE_90);
|
||||
StructureStart start = rigidSurfaceStart(List.of(lower, upper));
|
||||
BoundingBox area = new BoundingBox(10, 58, 10, 10, 72, 14);
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
for (int z = 10; z <= 14; z++) {
|
||||
put(blocks, 10, 61, z, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, 10, 62, z, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
}
|
||||
|
||||
Set<Long> written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(blocks), area, List.of(surfaceTarget(start)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager);
|
||||
|
||||
StructurePlaceSettings settings = new StructurePlaceSettings()
|
||||
.setRotation(Rotation.CLOCKWISE_90);
|
||||
BlockPos origin = upper.getPosition();
|
||||
BlockPos supportedBase = origin.offset(
|
||||
StructureTemplate.calculateRelativePosition(settings, BlockPos.ZERO));
|
||||
assertEquals(Set.of(supportedBase.below().asLong()), written);
|
||||
assertEquals(Blocks.DIRT.defaultBlockState(), state(
|
||||
blocks, supportedBase.getX(), supportedBase.getY() - 1, supportedBase.getZ()));
|
||||
for (int localX = 1; localX <= 4; localX++) {
|
||||
BlockPos vetoedBase = origin.offset(StructureTemplate.calculateRelativePosition(
|
||||
settings, new BlockPos(localX, 0, 0)));
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(
|
||||
blocks, vetoedBase.getX(), vetoedBase.getY() - 1, vetoedBase.getZ()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listElementsUseDeclaredOverlayOrderForLowestAuthoredCells() throws Exception {
|
||||
InlineLegacyPoolElement legacy = new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState()),
|
||||
block(1, 0, 0, Blocks.COBBLESTONE.defaultBlockState()))));
|
||||
InlineSinglePoolElement single = new InlineSinglePoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState()),
|
||||
block(1, 0, 0, Blocks.STONE.defaultBlockState()))));
|
||||
ListPoolElement list = new ListPoolElement(
|
||||
List.of(legacy, single), StructureTemplatePool.Projection.RIGID);
|
||||
PoolElementStructurePiece lower = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState())))),
|
||||
new BoundingBox(0, 62, 0, 1, 65, 0), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece upper = rigidTemplatePiece(
|
||||
list, new BoundingBox(0, 64, 0, 1, 70, 0), 1, Rotation.NONE);
|
||||
StructureStart start = rigidSurfaceStart(List.of(lower, upper));
|
||||
BoundingBox area = new BoundingBox(0, 58, 0, 1, 72, 0);
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
for (int x = 0; x <= 1; x++) {
|
||||
put(blocks, x, 61, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, x, 62, 0, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
}
|
||||
|
||||
Set<Long> written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(blocks), area, List.of(surfaceTarget(start)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager);
|
||||
|
||||
assertEquals(Set.of(BlockPos.asLong(1, 63, 0)), written);
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, 63, 0));
|
||||
assertEquals(Blocks.DIRT.defaultBlockState(), state(blocks, 1, 63, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void surfaceSupportIsChunkClippedAndStableAcrossSplitAreas() throws Exception {
|
||||
StructureTemplate upperTemplate = template(List.of(
|
||||
block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState()),
|
||||
block(1, 0, 0, Blocks.COBBLESTONE.defaultBlockState())));
|
||||
PoolElementStructurePiece lower = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState())))),
|
||||
new BoundingBox(15, 62, 0, 16, 65, 0), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece upper = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(upperTemplate),
|
||||
new BoundingBox(15, 64, 0, 16, 70, 0), 1, Rotation.NONE);
|
||||
StructureStart start = rigidSurfaceStart(List.of(lower, upper));
|
||||
BoundingBox wideArea = new BoundingBox(0, 58, 0, 31, 72, 15);
|
||||
BoundingBox westArea = new BoundingBox(0, 58, 0, 15, 72, 15);
|
||||
BoundingBox eastArea = new BoundingBox(16, 58, 0, 31, 72, 15);
|
||||
Map<BlockPos, BlockState> wideBlocks = supportTerrain(15, 16);
|
||||
Map<BlockPos, BlockState> splitBlocks = supportTerrain(15, 16);
|
||||
|
||||
Set<Long> wide = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(wideBlocks), wideArea, List.of(surfaceTarget(start)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager);
|
||||
Set<Long> split = new HashSet<>(
|
||||
NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(splitBlocks), westArea, List.of(surfaceTarget(start)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager));
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(splitBlocks, 16, 63, 0));
|
||||
split.addAll(NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(splitBlocks), eastArea, List.of(surfaceTarget(start)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager));
|
||||
|
||||
assertEquals(Set.of(
|
||||
BlockPos.asLong(15, 63, 0), BlockPos.asLong(16, 63, 0)), wide);
|
||||
assertEquals(wide, split);
|
||||
assertEquals(wideBlocks, splitBlocks);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportNeverPairsRigidAnchorsAcrossStarts() throws Exception {
|
||||
PoolElementStructurePiece lower = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState())))),
|
||||
new BoundingBox(0, 62, 0, 0, 65, 0), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece upper = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState())))),
|
||||
new BoundingBox(0, 64, 0, 0, 70, 0), 1, Rotation.NONE);
|
||||
StructureStart lowerStart = rigidSurfaceStart(List.of(lower));
|
||||
StructureStart upperStart = rigidSurfaceStart(List.of(upper));
|
||||
BoundingBox area = new BoundingBox(0, 58, 0, 0, 72, 0);
|
||||
Map<BlockPos, BlockState> blocks = supportTerrain(0, 0);
|
||||
|
||||
assertTrue(NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(blocks), area, List.of(surfaceTarget(lowerStart)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager).isEmpty());
|
||||
assertTrue(NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(blocks), area, List.of(surfaceTarget(upperStart)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager).isEmpty());
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, 63, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vacuumSupportsUnauthoredTerrainModesButLongMeetGapsRemainOpen() throws Exception {
|
||||
StructureTemplate upperTemplate = template(List.of(
|
||||
block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState())));
|
||||
PoolElementStructurePiece lower = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState())))),
|
||||
new BoundingBox(0, 62, 0, 0, 65, 0), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece upper = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(upperTemplate),
|
||||
new BoundingBox(0, 64, 0, 0, 70, 0), 1, Rotation.NONE);
|
||||
StructureStart vacuumStart = rigidSurfaceStart(
|
||||
List.of(lower, upper), TerrainAdjustment.NONE);
|
||||
BoundingBox area = new BoundingBox(0, 58, 0, 0, 72, 0);
|
||||
Map<BlockPos, BlockState> vacuumBlocks = supportTerrain(0, 0);
|
||||
|
||||
Set<Long> vacuumWritten = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(vacuumBlocks), area,
|
||||
List.of(surfaceTarget(vacuumStart, IrisStructureTerrainMode.VACUUM)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager);
|
||||
|
||||
assertEquals(Set.of(BlockPos.asLong(0, 63, 0)), vacuumWritten);
|
||||
assertTrue(NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(supportTerrain(0, 0)), area,
|
||||
List.of(surfaceTarget(vacuumStart, IrisStructureTerrainMode.SOURCE)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager).isEmpty());
|
||||
|
||||
PoolElementStructurePiece distantUpper = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(upperTemplate),
|
||||
new BoundingBox(0, 65, 0, 0, 71, 0), 1, Rotation.NONE);
|
||||
StructureStart distantStart = rigidSurfaceStart(
|
||||
List.of(lower, distantUpper), TerrainAdjustment.NONE);
|
||||
assertTrue(NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(supportTerrain(0, 0)), area,
|
||||
List.of(surfaceTarget(distantStart, IrisStructureTerrainMode.VACUUM)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void threeRigidPlanesCannotBuildAnUpwardSupportChain() throws Exception {
|
||||
PoolElementStructurePiece lower = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState())))),
|
||||
new BoundingBox(0, 62, 0, 0, 65, 0), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece middle = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState())))),
|
||||
new BoundingBox(0, 64, 0, 0, 69, 0), 0, Rotation.NONE);
|
||||
PoolElementStructurePiece upper = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.STONE.defaultBlockState())))),
|
||||
new BoundingBox(0, 65, 0, 0, 70, 0), 0, Rotation.NONE);
|
||||
StructureStart start = rigidSurfaceStart(List.of(lower, middle, upper));
|
||||
BoundingBox area = new BoundingBox(0, 58, 0, 0, 72, 0);
|
||||
Map<BlockPos, BlockState> blocks = supportTerrain(0, 0);
|
||||
|
||||
Set<Long> written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(blocks), area, List.of(surfaceTarget(start)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager);
|
||||
|
||||
assertEquals(Set.of(BlockPos.asLong(0, 63, 0)), written);
|
||||
assertEquals(Blocks.DIRT.defaultBlockState(), state(blocks, 0, 63, 0));
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, 64, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reversingTargetsCannotCreateCrossStartSupportChains() throws Exception {
|
||||
PoolElementStructurePiece ground = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState())))),
|
||||
new BoundingBox(0, 62, 0, 0, 65, 0), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece middle = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState())))),
|
||||
new BoundingBox(0, 64, 0, 0, 69, 0), 0, Rotation.NONE);
|
||||
PoolElementStructurePiece raisedGround = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState())))),
|
||||
new BoundingBox(0, 64, 0, 0, 69, 0), 0, Rotation.NONE);
|
||||
PoolElementStructurePiece upper = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.STONE.defaultBlockState())))),
|
||||
new BoundingBox(0, 65, 0, 0, 70, 0), 0, Rotation.NONE);
|
||||
StructureStart lowerStart = rigidSurfaceStart(List.of(ground, middle));
|
||||
StructureStart upperStart = rigidSurfaceStart(List.of(raisedGround, upper));
|
||||
BoundingBox area = new BoundingBox(0, 58, 0, 0, 72, 0);
|
||||
Map<BlockPos, BlockState> forwardBlocks = supportTerrain(0, 0);
|
||||
Map<BlockPos, BlockState> reverseBlocks = supportTerrain(0, 0);
|
||||
NativeStructureTerrainIntegrator.TerrainTarget lowerTarget = surfaceTarget(lowerStart);
|
||||
NativeStructureTerrainIntegrator.TerrainTarget upperTarget = surfaceTarget(upperStart);
|
||||
|
||||
Set<Long> forward = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(forwardBlocks), area, List.of(lowerTarget, upperTarget),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager);
|
||||
Set<Long> reverse = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(reverseBlocks), area, List.of(upperTarget, lowerTarget),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager);
|
||||
|
||||
assertEquals(Set.of(BlockPos.asLong(0, 63, 0)), forward);
|
||||
assertEquals(forward, reverse);
|
||||
assertEquals(forwardBlocks, reverseBlocks);
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(forwardBlocks, 0, 64, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preserveDuplicateCannotSuppressVacuumSupport() throws Exception {
|
||||
PoolElementStructurePiece lower = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState())))),
|
||||
new BoundingBox(0, 62, 0, 0, 65, 0), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece upper = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState())))),
|
||||
new BoundingBox(0, 64, 0, 0, 70, 0), 1, Rotation.NONE);
|
||||
StructureStart start = rigidSurfaceStart(
|
||||
List.of(lower, upper), TerrainAdjustment.NONE);
|
||||
BoundingBox area = new BoundingBox(0, 58, 0, 0, 72, 0);
|
||||
Map<BlockPos, BlockState> blocks = supportTerrain(0, 0);
|
||||
|
||||
Set<Long> written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(blocks), area, List.of(
|
||||
surfaceTarget(start, IrisStructureTerrainMode.PRESERVE),
|
||||
surfaceTarget(start, IrisStructureTerrainMode.VACUUM)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager);
|
||||
|
||||
assertEquals(Set.of(BlockPos.asLong(0, 63, 0)), written);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processorCreatedAirAndFluidVetoSurfaceSupport() throws Exception {
|
||||
StructureTemplate upperTemplate = template(List.of(
|
||||
block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState()),
|
||||
block(1, 0, 0, Blocks.STONE.defaultBlockState())));
|
||||
InlineSinglePoolElement upperElement = new InlineSinglePoolElement(
|
||||
upperTemplate, List.of(
|
||||
new ReplaceBlockProcessor(
|
||||
Blocks.COBBLESTONE.defaultBlockState(),
|
||||
Blocks.AIR.defaultBlockState()),
|
||||
new ReplaceBlockProcessor(
|
||||
Blocks.STONE.defaultBlockState(),
|
||||
Blocks.WATER.defaultBlockState())));
|
||||
PoolElementStructurePiece lower = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState())))),
|
||||
new BoundingBox(0, 62, 0, 1, 65, 0), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece upper = rigidTemplatePiece(
|
||||
upperElement, new BoundingBox(0, 64, 0, 1, 70, 0),
|
||||
1, Rotation.NONE);
|
||||
StructureStart start = rigidSurfaceStart(List.of(lower, upper));
|
||||
BoundingBox area = new BoundingBox(0, 58, 0, 1, 72, 0);
|
||||
Map<BlockPos, BlockState> blocks = supportTerrain(0, 1);
|
||||
|
||||
Set<Long> written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(blocks), area, List.of(surfaceTarget(start)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager);
|
||||
|
||||
assertTrue(written.isEmpty());
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, 63, 0));
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 1, 63, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void solidJigsawFinalStateCreatesSurfaceSupport() throws Exception {
|
||||
CompoundTag jigsawData = new CompoundTag();
|
||||
jigsawData.putString("final_state", "minecraft:cobblestone");
|
||||
StructureTemplate upperTemplate = template(List.of(
|
||||
new StructureTemplate.StructureBlockInfo(
|
||||
BlockPos.ZERO, Blocks.JIGSAW.defaultBlockState(), jigsawData)));
|
||||
PoolElementStructurePiece lower = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState())))),
|
||||
new BoundingBox(0, 62, 0, 0, 65, 0), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece upper = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(upperTemplate),
|
||||
new BoundingBox(0, 64, 0, 0, 70, 0), 1, Rotation.NONE);
|
||||
StructureStart start = rigidSurfaceStart(List.of(lower, upper));
|
||||
BoundingBox area = new BoundingBox(0, 58, 0, 0, 72, 0);
|
||||
Map<BlockPos, BlockState> blocks = supportTerrain(0, 0);
|
||||
|
||||
Set<Long> written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(blocks), area, List.of(surfaceTarget(start)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager);
|
||||
|
||||
assertEquals(Set.of(BlockPos.asLong(0, 63, 0)), written);
|
||||
assertEquals(Blocks.DIRT.defaultBlockState(), state(blocks, 0, 63, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void laterLegacyAirDoesNotOverlayEarlierListSolid() throws Exception {
|
||||
InlineSinglePoolElement solid = new InlineSinglePoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState()))));
|
||||
InlineLegacyPoolElement legacyAir = new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState()))));
|
||||
ListPoolElement list = new ListPoolElement(
|
||||
List.of(solid, legacyAir), StructureTemplatePool.Projection.RIGID);
|
||||
PoolElementStructurePiece lower = rigidTemplatePiece(
|
||||
new InlineLegacyPoolElement(template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState())))),
|
||||
new BoundingBox(0, 62, 0, 0, 65, 0), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece upper = rigidTemplatePiece(
|
||||
list, new BoundingBox(0, 64, 0, 0, 70, 0), 1, Rotation.NONE);
|
||||
StructureStart start = rigidSurfaceStart(List.of(lower, upper));
|
||||
BoundingBox area = new BoundingBox(0, 58, 0, 0, 72, 0);
|
||||
Map<BlockPos, BlockState> blocks = supportTerrain(0, 0);
|
||||
|
||||
Set<Long> written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world(blocks), area, List.of(surfaceTarget(start)),
|
||||
NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager);
|
||||
|
||||
assertEquals(Set.of(BlockPos.asLong(0, 63, 0)), written);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stackedRigidPiecesPreserveTheLowerAuthoredSurface() {
|
||||
Structure structure = new DesertPyramidStructure(
|
||||
@@ -286,12 +713,11 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
}
|
||||
}
|
||||
|
||||
WorldGenLevel world = world(blocks);
|
||||
List<NativeStructureTerrainIntegrator.TerrainTarget> targets =
|
||||
List.of(surfaceTarget(start));
|
||||
NativeStructureSurfaceFitter.prepareSurfaceStructures(
|
||||
world(blocks), area,
|
||||
List.of(new NativeStructureTerrainIntegrator.TerrainTarget(
|
||||
"nova_structures:tavern_oak", start,
|
||||
new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE))),
|
||||
(x, z) -> 60);
|
||||
world, area, targets, (x, z) -> 60);
|
||||
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 2, 62, 2));
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 2, 65, 2));
|
||||
@@ -519,7 +945,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeVacuumClearsEveryPieceEnvelopeBeforePlacement() {
|
||||
public void nativeVacuumLeavesPieceBlocksForSurfaceFitting() {
|
||||
StructureStart start = desertStart();
|
||||
BoundingBox bounds = start.getPieces().getFirst().getBoundingBox();
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
@@ -529,7 +955,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
world(blocks), bounds, "minecraft:desert_pyramid", start,
|
||||
new IrisStructureTerrain().setMode(IrisStructureTerrainMode.VACUUM), null);
|
||||
|
||||
assertEquals(Blocks.AIR.defaultBlockState(),
|
||||
assertEquals(Blocks.STONE.defaultBlockState(),
|
||||
state(blocks, bounds.minX(), bounds.minY(), bounds.minZ()));
|
||||
}
|
||||
|
||||
@@ -892,6 +1318,27 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
assertEquals(generated.getPieces().size(), wrapped.getPieces().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeVacuumReservesItsFixedSurfaceFalloff() {
|
||||
StructureStart generated = desertStart(TerrainAdjustment.NONE);
|
||||
BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(generated);
|
||||
IrisStructureTerrain terrain = new IrisStructureTerrain()
|
||||
.setMode(IrisStructureTerrainMode.VACUUM)
|
||||
.setHorizontalPadding(64);
|
||||
|
||||
BoundingBox references = NativeStructureReferenceEnvelope.referenceBounds(
|
||||
generated, generated.getStructure(), terrain);
|
||||
|
||||
assertEquals(content.minX() - NativeStructureSurfaceFitter.surfaceTerrainRadius(),
|
||||
references.minX());
|
||||
assertEquals(content.maxX() + NativeStructureSurfaceFitter.surfaceTerrainRadius(),
|
||||
references.maxX());
|
||||
assertEquals(content.minZ() - NativeStructureSurfaceFitter.surfaceTerrainRadius(),
|
||||
references.minZ());
|
||||
assertEquals(content.maxZ() + NativeStructureSurfaceFitter.surfaceTerrainRadius(),
|
||||
references.maxZ());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeTerrainEnvelopeClipsOptionalCoverageWithoutDroppingContent() {
|
||||
StructureStart generated = desertStart();
|
||||
@@ -999,7 +1446,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
BlockState log = Blocks.OAK_LOG.defaultBlockState();
|
||||
blocks.put(origin, log);
|
||||
|
||||
NativeStructureTerrainIntegrator.clearTemplateAir(world(blocks), template, origin, 80, settings);
|
||||
NativeStructureTerrainIntegrator.clearTemplateAir(
|
||||
world(blocks), template, origin, 80, settings);
|
||||
|
||||
assertEquals(log, blocks.get(origin));
|
||||
}
|
||||
@@ -1018,6 +1466,71 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
LiquidSettings.APPLY_WATERLOGGING);
|
||||
}
|
||||
|
||||
private static PoolElementStructurePiece rigidTemplatePiece(
|
||||
StructurePoolElement element, BoundingBox bounds,
|
||||
int groundLevelDelta, Rotation rotation) {
|
||||
return new PoolElementStructurePiece(
|
||||
null, element,
|
||||
new BlockPos(bounds.minX(), bounds.minY(), bounds.minZ()),
|
||||
groundLevelDelta, rotation, bounds,
|
||||
LiquidSettings.APPLY_WATERLOGGING);
|
||||
}
|
||||
|
||||
private static StructureStart rigidSurfaceStart(List<PoolElementStructurePiece> pieces) {
|
||||
return rigidSurfaceStart(pieces, TerrainAdjustment.BEARD_BOX);
|
||||
}
|
||||
|
||||
private static StructureStart rigidSurfaceStart(
|
||||
List<PoolElementStructurePiece> pieces, TerrainAdjustment adjustment) {
|
||||
Structure structure = new DesertPyramidStructure(
|
||||
new Structure.StructureSettings(
|
||||
HolderSet.empty(), Map.of(),
|
||||
GenerationStep.Decoration.SURFACE_STRUCTURES,
|
||||
adjustment));
|
||||
List<StructurePiece> structurePieces = new ArrayList<>(pieces);
|
||||
return new StructureStart(
|
||||
structure, new ChunkPos(0, 0), 0,
|
||||
new PiecesContainer(structurePieces));
|
||||
}
|
||||
|
||||
private static NativeStructureTerrainIntegrator.TerrainTarget surfaceTarget(
|
||||
StructureStart start) {
|
||||
return surfaceTarget(start, IrisStructureTerrainMode.SOURCE);
|
||||
}
|
||||
|
||||
private static NativeStructureTerrainIntegrator.TerrainTarget surfaceTarget(
|
||||
StructureStart start, IrisStructureTerrainMode mode) {
|
||||
return new NativeStructureTerrainIntegrator.TerrainTarget(
|
||||
"nova_structures:tavern_oak", start,
|
||||
new IrisStructureTerrain().setMode(mode));
|
||||
}
|
||||
|
||||
private static StructureTemplate.StructureBlockInfo block(
|
||||
int x, int y, int z, BlockState state) {
|
||||
return new StructureTemplate.StructureBlockInfo(
|
||||
new BlockPos(x, y, z), state, null);
|
||||
}
|
||||
|
||||
private static Map<BlockPos, BlockState> supportTerrain(int minimumX, int maximumX) {
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
for (int x = minimumX; x <= maximumX; x++) {
|
||||
put(blocks, x, 61, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, x, 62, 0, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
private static Map<BlockPos, BlockState> flatTerrain(BoundingBox area, int surfaceY) {
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
for (int x = area.minX(); x <= area.maxX(); x++) {
|
||||
for (int z = area.minZ(); z <= area.maxZ(); z++) {
|
||||
put(blocks, x, surfaceY - 1, z, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, x, surfaceY, z, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
private static NativeStructureTerrainIntegrator.OrganicCarve organicCarve(
|
||||
StructureStart start, int horizontalPadding) {
|
||||
return organicCarve(start, horizontalPadding, 0.85D);
|
||||
@@ -1122,6 +1635,57 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
return template;
|
||||
}
|
||||
|
||||
private static final class InlineSinglePoolElement extends SinglePoolElement {
|
||||
private InlineSinglePoolElement(StructureTemplate template) {
|
||||
this(template, List.of());
|
||||
}
|
||||
|
||||
private InlineSinglePoolElement(
|
||||
StructureTemplate template, List<StructureProcessor> processors) {
|
||||
super(Either.right(template),
|
||||
Holder.direct(new StructureProcessorList(processors)),
|
||||
StructureTemplatePool.Projection.RIGID,
|
||||
Optional.<LiquidSettings>empty());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class InlineLegacyPoolElement extends LegacySinglePoolElement {
|
||||
private InlineLegacyPoolElement(StructureTemplate template) {
|
||||
super(Either.right(template),
|
||||
Holder.direct(new StructureProcessorList(List.of())),
|
||||
StructureTemplatePool.Projection.RIGID,
|
||||
Optional.<LiquidSettings>empty());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ReplaceBlockProcessor implements StructureProcessor {
|
||||
private final BlockState source;
|
||||
private final BlockState replacement;
|
||||
|
||||
private ReplaceBlockProcessor(BlockState source, BlockState replacement) {
|
||||
this.source = source;
|
||||
this.replacement = replacement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureTemplate.StructureBlockInfo processBlock(
|
||||
LevelReader level, BlockPos targetPosition, BlockPos referencePos,
|
||||
BlockPos templateRelativePos,
|
||||
StructureTemplate.StructureBlockInfo processedBlockInfo,
|
||||
StructurePlaceSettings settings) {
|
||||
if (!processedBlockInfo.state().equals(source)) {
|
||||
return processedBlockInfo;
|
||||
}
|
||||
return new StructureTemplate.StructureBlockInfo(
|
||||
processedBlockInfo.pos(), replacement, processedBlockInfo.nbt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapCodec<? extends StructureProcessor> codec() {
|
||||
return BlockIgnoreProcessor.STRUCTURE_BLOCK.codec();
|
||||
}
|
||||
}
|
||||
|
||||
private static StructureStart desertStart() {
|
||||
return desertStart(TerrainAdjustment.NONE);
|
||||
}
|
||||
@@ -1155,6 +1719,9 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
if (methodName.equals("getLevel")) {
|
||||
return null;
|
||||
}
|
||||
if (methodName.equals("holderLookup")) {
|
||||
return BuiltInRegistries.BLOCK;
|
||||
}
|
||||
if (methodName.equals("hashCode")) {
|
||||
return System.identityHashCode(proxy);
|
||||
}
|
||||
@@ -1187,6 +1754,9 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
if (methodName.equals("getLevel")) {
|
||||
return null;
|
||||
}
|
||||
if (methodName.equals("holderLookup")) {
|
||||
return BuiltInRegistries.BLOCK;
|
||||
}
|
||||
if (methodName.equals("hashCode")) {
|
||||
return System.identityHashCode(proxy);
|
||||
}
|
||||
|
||||
+40
-2
@@ -7,6 +7,8 @@ import art.arcane.iris.engine.framework.NativeStructureOwnershipStore;
|
||||
import art.arcane.iris.engine.framework.NativeStructurePlacementPlanner;
|
||||
import art.arcane.iris.engine.framework.NativeStructureStartPlan;
|
||||
import art.arcane.iris.engine.framework.StructurePlacementGrid;
|
||||
import art.arcane.iris.engine.object.IrisStructureTerrain;
|
||||
import art.arcane.iris.engine.object.IrisStructureTerrainMode;
|
||||
import art.arcane.iris.engine.object.NativeStructureGenerationStatus;
|
||||
import art.arcane.iris.engine.object.NativeStructureSuppression;
|
||||
import net.minecraft.core.Holder;
|
||||
@@ -44,8 +46,13 @@ public final class NativeStructureOwnershipRecovery {
|
||||
NativeStructureOwnershipRecord persisted = NativeStructureOwnershipStore.findPersisted(
|
||||
engine, structureKey, origin.x(), origin.z());
|
||||
if (persisted != null) {
|
||||
if (NativeStructureOwnershipFingerprint.matches(persisted, start)) {
|
||||
return persisted;
|
||||
NativeStructureOwnershipRecord refreshed = refreshReferenceEnvelope(
|
||||
structureKey, activeStructure, start, persisted);
|
||||
if (refreshed != null) {
|
||||
if (refreshed != persisted) {
|
||||
NativeStructureOwnershipStore.record(engine, refreshed);
|
||||
}
|
||||
return refreshed;
|
||||
}
|
||||
NativeStructureOwnershipStore.discard(
|
||||
engine, structureKey, origin.x(), origin.z());
|
||||
@@ -72,6 +79,37 @@ public final class NativeStructureOwnershipRecovery {
|
||||
return recovered;
|
||||
}
|
||||
|
||||
static NativeStructureOwnershipRecord refreshReferenceEnvelope(
|
||||
String structureKey, Structure structure, StructureStart start,
|
||||
NativeStructureOwnershipRecord ownership) {
|
||||
if (structure == null || start == null || !start.isValid()
|
||||
|| start.getStructure() != structure || ownership == null
|
||||
|| !ownership.structureKey().equals(normalize(structureKey))
|
||||
|| !NativeStructureOwnershipFingerprint.matches(ownership, start)) {
|
||||
return null;
|
||||
}
|
||||
IrisStructureTerrain terrain = NativeStructureTerrainIntegrator.resolveNativeTerrain(
|
||||
start, ownership.restoredDecision().terrain());
|
||||
if (terrain.resolvedMode() != IrisStructureTerrainMode.VACUUM) {
|
||||
return ownership;
|
||||
}
|
||||
BoundingBox expected = NativeStructureReferenceEnvelope.referenceBounds(
|
||||
start, structure, terrain, structureKey);
|
||||
int referenceMinChunkX = expected.minX() >> 4;
|
||||
int referenceMaxChunkX = expected.maxX() >> 4;
|
||||
int referenceMinChunkZ = expected.minZ() >> 4;
|
||||
int referenceMaxChunkZ = expected.maxZ() >> 4;
|
||||
if (ownership.referenceMinChunkX() == referenceMinChunkX
|
||||
&& ownership.referenceMaxChunkX() == referenceMaxChunkX
|
||||
&& ownership.referenceMinChunkZ() == referenceMinChunkZ
|
||||
&& ownership.referenceMaxChunkZ() == referenceMaxChunkZ) {
|
||||
return ownership;
|
||||
}
|
||||
return ownership.withReferenceEnvelope(
|
||||
referenceMinChunkX, referenceMaxChunkX,
|
||||
referenceMinChunkZ, referenceMaxChunkZ);
|
||||
}
|
||||
|
||||
static NativeStructureOwnershipRecord proveCandidate(
|
||||
String structureKey, Structure structure, StructureStart persisted,
|
||||
NativeStructureStartPlan plan, StructureStart expected,
|
||||
|
||||
+2
@@ -55,6 +55,8 @@ public final class NativeStructurePostProcessor {
|
||||
world, area, target.start(), () -> world.getLevel().getStructureManager());
|
||||
}
|
||||
}
|
||||
NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(
|
||||
world, area, targets, () -> world.getLevel().getStructureManager());
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
|
||||
+3
-1
@@ -64,7 +64,9 @@ public final class NativeStructureReferenceEnvelope {
|
||||
|| mode == IrisStructureTerrainMode.FORCE_CARVE
|
||||
|| mode == IrisStructureTerrainMode.VACUUM
|
||||
|| mode == IrisStructureTerrainMode.ENCASE;
|
||||
int horizontalPadding = usesEnvelope ? Math.max(0, terrain.getHorizontalPadding()) : 0;
|
||||
int horizontalPadding = mode == IrisStructureTerrainMode.VACUUM
|
||||
? NativeStructureSurfaceFitter.surfaceTerrainRadius()
|
||||
: usesEnvelope ? Math.max(0, terrain.getHorizontalPadding()) : 0;
|
||||
BoundingBox content = contentBounds(start);
|
||||
if (!fitsReferenceRange(start.getChunkPos(), content)) {
|
||||
throw new UnrepresentableContentException("Native structure content at "
|
||||
|
||||
+204
@@ -1,15 +1,27 @@
|
||||
package art.arcane.iris.nativegen;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.levelgen.structure.ScatteredFeaturePiece;
|
||||
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||
import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.LiquidSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.structures.OceanMonumentPieces;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
@@ -100,6 +112,63 @@ final class NativeStructureReflection {
|
||||
return resolveTemplateReference(reference, templates);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<StructureTemplate.StructureBlockInfo> resolveTemplateBlocks(
|
||||
StructureTemplate template, StructurePlaceSettings settings,
|
||||
BlockPos origin) {
|
||||
Object value;
|
||||
try {
|
||||
value = StructureTemplatePalettesAccess.FIELD.get(template);
|
||||
} catch (IllegalAccessException error) {
|
||||
throw new IllegalStateException("Cannot read native structure template palettes", error);
|
||||
}
|
||||
if (!(value instanceof List<?> paletteValues)) {
|
||||
throw new IllegalStateException("Native structure template palettes field is not a List");
|
||||
}
|
||||
List<StructureTemplate.Palette> palettes =
|
||||
(List<StructureTemplate.Palette>) paletteValues;
|
||||
if (palettes.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
return settings.getRandomPalette(palettes, origin).blocks();
|
||||
}
|
||||
|
||||
static StructurePlaceSettings resolvePlacementSettings(
|
||||
SinglePoolElement element, PoolElementStructurePiece piece,
|
||||
BoundingBox area) {
|
||||
LiquidSettings liquidSettings;
|
||||
try {
|
||||
liquidSettings = (LiquidSettings) PoolPieceLiquidSettingsAccess.FIELD.get(piece);
|
||||
} catch (IllegalAccessException error) {
|
||||
throw new IllegalStateException("Cannot read native structure piece liquid settings", error);
|
||||
}
|
||||
try {
|
||||
return (StructurePlaceSettings) SinglePoolSettingsAccess.METHOD.invoke(
|
||||
element, piece.getRotation(), area, liquidSettings, false);
|
||||
} catch (IllegalAccessException | InvocationTargetException error) {
|
||||
throw new IllegalStateException("Cannot create native structure placement settings", error);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<StructureTemplate.StructureBlockInfo> processTemplateBlocks(
|
||||
ServerLevelAccessor world, BlockPos origin, BlockPos referencePos,
|
||||
StructurePlaceSettings settings,
|
||||
List<StructureTemplate.StructureBlockInfo> blocks,
|
||||
StructureTemplate template) {
|
||||
Method forgeMethod = StructureTemplateProcessingAccess.FORGE_METHOD;
|
||||
if (forgeMethod == null) {
|
||||
return StructureTemplate.processBlockInfos(
|
||||
world, origin, referencePos, settings, blocks);
|
||||
}
|
||||
try {
|
||||
return (List<StructureTemplate.StructureBlockInfo>) forgeMethod.invoke(
|
||||
null, world, origin, referencePos, settings, blocks, template);
|
||||
} catch (IllegalAccessException | InvocationTargetException error) {
|
||||
throw new IllegalStateException("Cannot process native structure template blocks", error);
|
||||
}
|
||||
}
|
||||
|
||||
static StructureTemplate resolveTemplateReference(Either<?, ?> reference,
|
||||
Supplier<StructureTemplateManager> templates) {
|
||||
return reference.map(
|
||||
@@ -125,6 +194,113 @@ final class NativeStructureReflection {
|
||||
return template;
|
||||
}
|
||||
|
||||
private static Field resolveStructureTemplatePalettesField() {
|
||||
Field resolved = null;
|
||||
for (Field field : StructureTemplate.class.getDeclaredFields()) {
|
||||
if (Modifier.isStatic(field.getModifiers()) || field.getType() != List.class
|
||||
|| !isPaletteList(field.getGenericType())) {
|
||||
continue;
|
||||
}
|
||||
if (resolved != null) {
|
||||
throw new IllegalStateException("StructureTemplate has multiple palette List fields");
|
||||
}
|
||||
resolved = field;
|
||||
}
|
||||
if (resolved == null) {
|
||||
throw new IllegalStateException("StructureTemplate palette List field is missing");
|
||||
}
|
||||
if (!Modifier.isFinal(resolved.getModifiers())) {
|
||||
throw new IllegalStateException("StructureTemplate palette field has an unexpected access contract");
|
||||
}
|
||||
if (!resolved.trySetAccessible()) {
|
||||
throw new IllegalStateException("StructureTemplate palette field is inaccessible");
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
private static boolean isPaletteList(Type type) {
|
||||
if (!(type instanceof ParameterizedType parameterized)) {
|
||||
return false;
|
||||
}
|
||||
Type[] arguments = parameterized.getActualTypeArguments();
|
||||
return arguments.length == 1 && arguments[0] == StructureTemplate.Palette.class;
|
||||
}
|
||||
|
||||
private static Method resolveSinglePoolSettingsMethod() {
|
||||
Method resolved = null;
|
||||
Class<?>[] parameterTypes = {
|
||||
Rotation.class, BoundingBox.class, LiquidSettings.class, boolean.class
|
||||
};
|
||||
for (Method method : SinglePoolElement.class.getDeclaredMethods()) {
|
||||
if (Modifier.isStatic(method.getModifiers())
|
||||
|| method.getReturnType() != StructurePlaceSettings.class
|
||||
|| !Arrays.equals(method.getParameterTypes(), parameterTypes)) {
|
||||
continue;
|
||||
}
|
||||
if (resolved != null) {
|
||||
throw new IllegalStateException("SinglePoolElement has multiple placement-settings methods");
|
||||
}
|
||||
resolved = method;
|
||||
}
|
||||
if (resolved == null) {
|
||||
throw new IllegalStateException("SinglePoolElement placement-settings method is missing");
|
||||
}
|
||||
if (!Modifier.isProtected(resolved.getModifiers())) {
|
||||
throw new IllegalStateException("SinglePoolElement placement-settings method has an unexpected access contract");
|
||||
}
|
||||
if (!resolved.trySetAccessible()) {
|
||||
throw new IllegalStateException("SinglePoolElement placement-settings method is inaccessible");
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
private static Field resolvePoolPieceLiquidSettingsField() {
|
||||
Field resolved = null;
|
||||
for (Field field : PoolElementStructurePiece.class.getDeclaredFields()) {
|
||||
if (Modifier.isStatic(field.getModifiers())
|
||||
|| field.getType() != LiquidSettings.class) {
|
||||
continue;
|
||||
}
|
||||
if (resolved != null) {
|
||||
throw new IllegalStateException("PoolElementStructurePiece has multiple liquid-settings fields");
|
||||
}
|
||||
resolved = field;
|
||||
}
|
||||
if (resolved == null) {
|
||||
throw new IllegalStateException("PoolElementStructurePiece liquid-settings field is missing");
|
||||
}
|
||||
if (!Modifier.isPrivate(resolved.getModifiers())
|
||||
|| !Modifier.isFinal(resolved.getModifiers())) {
|
||||
throw new IllegalStateException("PoolElementStructurePiece liquid-settings field has an unexpected access contract");
|
||||
}
|
||||
if (!resolved.trySetAccessible()) {
|
||||
throw new IllegalStateException("PoolElementStructurePiece liquid-settings field is inaccessible");
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
private static Method resolveForgeTemplateProcessingMethod() {
|
||||
Class<?>[] parameterTypes = {
|
||||
ServerLevelAccessor.class, BlockPos.class, BlockPos.class,
|
||||
StructurePlaceSettings.class, List.class, StructureTemplate.class
|
||||
};
|
||||
Method resolved = null;
|
||||
for (Method method : StructureTemplate.class.getDeclaredMethods()) {
|
||||
if (!Modifier.isPublic(method.getModifiers())
|
||||
|| !Modifier.isStatic(method.getModifiers())
|
||||
|| method.getReturnType() != List.class
|
||||
|| !Arrays.equals(method.getParameterTypes(), parameterTypes)) {
|
||||
continue;
|
||||
}
|
||||
if (resolved != null) {
|
||||
throw new IllegalStateException(
|
||||
"StructureTemplate has multiple Forge block-processing methods");
|
||||
}
|
||||
resolved = method;
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
static final class MonumentChildPiecesAccess {
|
||||
static final Field FIELD = resolveMonumentChildPiecesField();
|
||||
|
||||
@@ -145,4 +321,32 @@ final class NativeStructureReflection {
|
||||
private SinglePoolTemplateAccess() {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class StructureTemplatePalettesAccess {
|
||||
private static final Field FIELD = resolveStructureTemplatePalettesField();
|
||||
|
||||
private StructureTemplatePalettesAccess() {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SinglePoolSettingsAccess {
|
||||
private static final Method METHOD = resolveSinglePoolSettingsMethod();
|
||||
|
||||
private SinglePoolSettingsAccess() {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PoolPieceLiquidSettingsAccess {
|
||||
private static final Field FIELD = resolvePoolPieceLiquidSettingsField();
|
||||
|
||||
private PoolPieceLiquidSettingsAccess() {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class StructureTemplateProcessingAccess {
|
||||
private static final Method FORGE_METHOD = resolveForgeTemplateProcessingMethod();
|
||||
|
||||
private StructureTemplateProcessingAccess() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-5
@@ -48,6 +48,10 @@ public final class NativeStructureSurfaceFitter {
|
||||
|| adjustment == TerrainAdjustment.BEARD_BOX;
|
||||
}
|
||||
|
||||
static int surfaceTerrainRadius() {
|
||||
return SURFACE_TERRAIN_RADIUS;
|
||||
}
|
||||
|
||||
static int resolveSurfaceTarget(List<SurfaceAnchor> anchors, int worldX, int worldZ,
|
||||
int originalY) {
|
||||
return resolveSurface(anchors, worldX, worldZ, originalY).targetY();
|
||||
@@ -160,7 +164,7 @@ public final class NativeStructureSurfaceFitter {
|
||||
continue;
|
||||
}
|
||||
StructureStart start = target.start();
|
||||
TerrainAdjustment adjustment = start.getStructure().terrainAdaptation();
|
||||
TerrainAdjustment adjustment = effectiveSurfaceAdjustment(target);
|
||||
for (StructurePiece piece : start.getPieces()) {
|
||||
if (piece instanceof PoolElementStructurePiece poolPiece) {
|
||||
if (poolPiece.getElement().getProjection() == StructureTemplatePool.Projection.RIGID) {
|
||||
@@ -203,17 +207,28 @@ public final class NativeStructureSurfaceFitter {
|
||||
}
|
||||
|
||||
static boolean requiresSurfaceTerrain(NativeStructureTerrainIntegrator.TerrainTarget target) {
|
||||
if (target == null || target.terrain() == null
|
||||
|| target.terrain().resolvedMode() != IrisStructureTerrainMode.SOURCE) {
|
||||
if (target == null || target.terrain() == null) {
|
||||
return false;
|
||||
}
|
||||
StructureStart start = target.start();
|
||||
return start != null
|
||||
&& start.isValid()
|
||||
if (start == null || !start.isValid()) {
|
||||
return false;
|
||||
}
|
||||
IrisStructureTerrainMode mode = target.terrain().resolvedMode();
|
||||
return mode == IrisStructureTerrainMode.VACUUM
|
||||
|| mode == IrisStructureTerrainMode.SOURCE
|
||||
&& shouldPrepareSurfaceTerrain(
|
||||
start.getStructure().terrainAdaptation(), start.getStructure().step());
|
||||
}
|
||||
|
||||
static TerrainAdjustment effectiveSurfaceAdjustment(
|
||||
NativeStructureTerrainIntegrator.TerrainTarget target) {
|
||||
if (target.terrain().resolvedMode() == IrisStructureTerrainMode.VACUUM) {
|
||||
return TerrainAdjustment.BEARD_THIN;
|
||||
}
|
||||
return target.start().getStructure().terrainAdaptation();
|
||||
}
|
||||
|
||||
private static void fitSurfaceTerrain(WorldGenLevel world, BoundingBox area,
|
||||
List<SurfaceAnchor> anchors,
|
||||
IntBinaryOperator surfaceHeight) {
|
||||
@@ -382,6 +397,7 @@ public final class NativeStructureSurfaceFitter {
|
||||
int verticalDistance(int y) {
|
||||
return IrisObjectVacuum.outset(y, minInfluenceY, maxInfluenceY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private record SurfaceMaterials(BlockState surface, BlockState subsurface) {
|
||||
|
||||
+405
@@ -0,0 +1,405 @@
|
||||
package art.arcane.iris.nativegen;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||
import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece;
|
||||
import net.minecraft.world.level.levelgen.structure.StructurePiece;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.EmptyPoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.ListPoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
final class NativeStructureSurfaceSupportBuilder {
|
||||
private static final int MAX_BATCH_CELLS = 524_288;
|
||||
private static final int MAX_BRIDGE_SPAN = 2;
|
||||
private static final OccupancyCell BLOCKER = new OccupancyCell(null, true);
|
||||
|
||||
private NativeStructureSurfaceSupportBuilder() {
|
||||
}
|
||||
|
||||
static Set<Long> bridgeRigidPieceSupport(
|
||||
WorldGenLevel world, BoundingBox area,
|
||||
List<NativeStructureTerrainIntegrator.TerrainTarget> targets,
|
||||
Supplier<StructureTemplateManager> templates) {
|
||||
if (targets == null || targets.isEmpty()) {
|
||||
return Set.of();
|
||||
}
|
||||
Map<Long, BlockState> planned;
|
||||
try {
|
||||
BatchCellBudget budget = new BatchCellBudget();
|
||||
List<SupportRequest> requests = new ArrayList<>();
|
||||
Set<StructureStart> seenStarts = Collections.newSetFromMap(
|
||||
new IdentityHashMap<>());
|
||||
for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) {
|
||||
if (target == null
|
||||
|| !NativeStructureSurfaceFitter.requiresSurfaceTerrain(target)
|
||||
|| !seenStarts.add(target.start())) {
|
||||
continue;
|
||||
}
|
||||
collectTargetRequests(
|
||||
world, area, target, templates, budget, requests);
|
||||
}
|
||||
planned = planWrites(world, area, requests, budget);
|
||||
} catch (BatchBudgetExceeded ignored) {
|
||||
return Set.of();
|
||||
}
|
||||
if (planned.isEmpty()) {
|
||||
return Set.of();
|
||||
}
|
||||
List<Map.Entry<Long, BlockState>> ordered = new ArrayList<>(planned.entrySet());
|
||||
ordered.sort(Map.Entry.comparingByKey());
|
||||
Set<Long> written = new HashSet<>();
|
||||
for (Map.Entry<Long, BlockState> entry : ordered) {
|
||||
BlockPos position = BlockPos.of(entry.getKey());
|
||||
BlockState existing = world.getBlockState(position);
|
||||
if (!existing.isSolid()
|
||||
&& !NativeStructureVegetationClearer.isTreeBlock(existing)
|
||||
&& existing.getFluidState().isEmpty()
|
||||
&& world.setBlock(position, entry.getValue(), 2)) {
|
||||
written.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
return Set.copyOf(written);
|
||||
}
|
||||
|
||||
private static void collectTargetRequests(
|
||||
WorldGenLevel world, BoundingBox area,
|
||||
NativeStructureTerrainIntegrator.TerrainTarget target,
|
||||
Supplier<StructureTemplateManager> templates,
|
||||
BatchCellBudget budget, List<SupportRequest> requests) {
|
||||
if (!NativeStructureSurfaceFitter.requiresSurfaceTerrain(target)) {
|
||||
return;
|
||||
}
|
||||
List<RigidAnchor> anchors = rigidAnchors(target.start());
|
||||
if (anchors.size() < 2) {
|
||||
return;
|
||||
}
|
||||
BlockPos referencePos = referencePosition(target.start());
|
||||
for (RigidAnchor upper : anchors) {
|
||||
Map<Long, Integer> lowerMeets = candidateLowerMeets(
|
||||
area, upper, anchors, budget);
|
||||
if (lowerMeets.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
Map<Long, OccupancyCell> occupancy = effectiveOccupancy(
|
||||
world, area, upper.piece(), lowerMeets,
|
||||
referencePos, templates, budget);
|
||||
Map<Long, LowestCell> lowest = lowestCells(occupancy);
|
||||
for (Map.Entry<Long, LowestCell> entry : lowest.entrySet()) {
|
||||
LowestCell cell = entry.getValue();
|
||||
Integer lowerMeet = lowerMeets.get(entry.getKey());
|
||||
if (lowerMeet == null || cell.occupancy().blocker()
|
||||
|| !isSolidBase(cell.occupancy().state())
|
||||
|| cell.y() - lowerMeet < 2
|
||||
|| cell.y() - lowerMeet > MAX_BRIDGE_SPAN) {
|
||||
continue;
|
||||
}
|
||||
budget.consume(1);
|
||||
requests.add(new SupportRequest(
|
||||
cell.x(), cell.z(), lowerMeet, cell.y()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<RigidAnchor> rigidAnchors(StructureStart start) {
|
||||
List<RigidAnchor> anchors = new ArrayList<>();
|
||||
for (StructurePiece piece : start.getPieces()) {
|
||||
if (!(piece instanceof PoolElementStructurePiece poolPiece)
|
||||
|| poolPiece.getElement().getProjection()
|
||||
!= StructureTemplatePool.Projection.RIGID) {
|
||||
continue;
|
||||
}
|
||||
BoundingBox bounds = poolPiece.getBoundingBox();
|
||||
int meetY = bounds.minY() + poolPiece.getGroundLevelDelta() - 1;
|
||||
anchors.add(new RigidAnchor(poolPiece, bounds, meetY));
|
||||
}
|
||||
return List.copyOf(anchors);
|
||||
}
|
||||
|
||||
private static Map<Long, Integer> candidateLowerMeets(
|
||||
BoundingBox area, RigidAnchor upper, List<RigidAnchor> anchors,
|
||||
BatchCellBudget budget) {
|
||||
Map<Long, Integer> lowerMeets = new HashMap<>();
|
||||
for (RigidAnchor lower : anchors) {
|
||||
budget.consume(1);
|
||||
int span = upper.meetY() - lower.meetY();
|
||||
if (lower.piece() == upper.piece() || span < 1
|
||||
|| span > MAX_BRIDGE_SPAN
|
||||
|| lower.meetY() < area.minY() || lower.meetY() > area.maxY()) {
|
||||
continue;
|
||||
}
|
||||
int minimumX = Math.max(area.minX(),
|
||||
Math.max(upper.bounds().minX(), lower.bounds().minX()));
|
||||
int maximumX = Math.min(area.maxX(),
|
||||
Math.min(upper.bounds().maxX(), lower.bounds().maxX()));
|
||||
int minimumZ = Math.max(area.minZ(),
|
||||
Math.max(upper.bounds().minZ(), lower.bounds().minZ()));
|
||||
int maximumZ = Math.min(area.maxZ(),
|
||||
Math.min(upper.bounds().maxZ(), lower.bounds().maxZ()));
|
||||
if (minimumX > maximumX || minimumZ > maximumZ) {
|
||||
continue;
|
||||
}
|
||||
for (int x = minimumX; x <= maximumX; x++) {
|
||||
for (int z = minimumZ; z <= maximumZ; z++) {
|
||||
budget.consume(1);
|
||||
lowerMeets.merge(columnKey(x, z), lower.meetY(), Math::max);
|
||||
}
|
||||
}
|
||||
}
|
||||
return lowerMeets;
|
||||
}
|
||||
|
||||
private static Map<Long, OccupancyCell> effectiveOccupancy(
|
||||
WorldGenLevel world, BoundingBox area,
|
||||
PoolElementStructurePiece piece, Map<Long, Integer> lowerMeets,
|
||||
BlockPos referencePos, Supplier<StructureTemplateManager> templates,
|
||||
BatchCellBudget budget) {
|
||||
List<SinglePoolElement> leaves = new ArrayList<>();
|
||||
if (!flattenSingles(piece.getElement(), leaves)) {
|
||||
return Map.of();
|
||||
}
|
||||
List<LeafPlacement> placements = new ArrayList<>(leaves.size());
|
||||
Map<Long, OccupancyCell> occupancy = new HashMap<>();
|
||||
for (SinglePoolElement leaf : leaves) {
|
||||
StructurePlaceSettings settings =
|
||||
NativeStructureReflection.resolvePlacementSettings(leaf, piece, area);
|
||||
StructureTemplate template = NativeStructureReflection.resolveTemplate(leaf, templates);
|
||||
List<StructureTemplate.StructureBlockInfo> rawBlocks =
|
||||
NativeStructureReflection.resolveTemplateBlocks(
|
||||
template, settings, piece.getPosition());
|
||||
budget.consume(rawBlocks.size());
|
||||
placements.add(new LeafPlacement(settings, rawBlocks, template));
|
||||
for (StructureTemplate.StructureBlockInfo raw : rawBlocks) {
|
||||
BlockPos position = piece.getPosition().offset(
|
||||
StructureTemplate.calculateRelativePosition(settings, raw.pos()));
|
||||
if (retain(position, area, lowerMeets)) {
|
||||
occupancy.put(position.asLong(), BLOCKER);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (LeafPlacement placement : placements) {
|
||||
List<StructureTemplate.StructureBlockInfo> processed =
|
||||
NativeStructureReflection.processTemplateBlocks(
|
||||
world, piece.getPosition(), referencePos,
|
||||
placement.settings(), placement.rawBlocks(),
|
||||
placement.template());
|
||||
budget.consume(processed.size());
|
||||
for (StructureTemplate.StructureBlockInfo block : processed) {
|
||||
BlockPos position = block.pos();
|
||||
if (!retain(position, area, lowerMeets)) {
|
||||
continue;
|
||||
}
|
||||
BlockState state = block.state()
|
||||
.mirror(placement.settings().getMirror())
|
||||
.rotate(placement.settings().getRotation());
|
||||
occupancy.put(position.asLong(), new OccupancyCell(state, false));
|
||||
}
|
||||
}
|
||||
return occupancy;
|
||||
}
|
||||
|
||||
private static boolean flattenSingles(
|
||||
StructurePoolElement element, List<SinglePoolElement> leaves) {
|
||||
if (element instanceof ListPoolElement listElement) {
|
||||
for (StructurePoolElement child : listElement.getElements()) {
|
||||
if (!flattenSingles(child, leaves)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (element == EmptyPoolElement.INSTANCE) {
|
||||
return true;
|
||||
}
|
||||
if (element instanceof SinglePoolElement singleElement) {
|
||||
leaves.add(singleElement);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean retain(
|
||||
BlockPos position, BoundingBox area, Map<Long, Integer> lowerMeets) {
|
||||
if (!area.isInside(position)) {
|
||||
return false;
|
||||
}
|
||||
Integer lowerMeet = lowerMeets.get(columnKey(position.getX(), position.getZ()));
|
||||
return lowerMeet != null && position.getY() <= lowerMeet + MAX_BRIDGE_SPAN;
|
||||
}
|
||||
|
||||
private static Map<Long, LowestCell> lowestCells(
|
||||
Map<Long, OccupancyCell> occupancy) {
|
||||
Map<Long, LowestCell> lowest = new HashMap<>();
|
||||
for (Map.Entry<Long, OccupancyCell> entry : occupancy.entrySet()) {
|
||||
BlockPos position = BlockPos.of(entry.getKey());
|
||||
long column = columnKey(position.getX(), position.getZ());
|
||||
LowestCell current = lowest.get(column);
|
||||
if (current == null || position.getY() < current.y()) {
|
||||
lowest.put(column, new LowestCell(
|
||||
position.getX(), position.getY(), position.getZ(),
|
||||
entry.getValue()));
|
||||
}
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
|
||||
private static Map<Long, BlockState> planWrites(
|
||||
WorldGenLevel world, BoundingBox area,
|
||||
List<SupportRequest> requests, BatchCellBudget budget) {
|
||||
List<SupportRequest> eligible = new ArrayList<>();
|
||||
Set<MaterialKey> materialKeys = new HashSet<>();
|
||||
BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos();
|
||||
for (SupportRequest request : requests) {
|
||||
BlockState lowerTerrain = world.getBlockState(position.set(
|
||||
request.x(), request.lowerMeetY(), request.z()));
|
||||
if (!isTerrainSupport(lowerTerrain)
|
||||
|| !gapIsClear(world, area, position, request, budget)) {
|
||||
continue;
|
||||
}
|
||||
eligible.add(request);
|
||||
materialKeys.add(new MaterialKey(
|
||||
request.x(), request.z(), request.lowerMeetY()));
|
||||
}
|
||||
Map<MaterialKey, BlockState> materials = new HashMap<>();
|
||||
for (MaterialKey key : materialKeys) {
|
||||
materials.put(key, resolveSupportState(
|
||||
world, position, key.x(), key.z(), key.lowerMeetY(), budget));
|
||||
}
|
||||
Map<Long, BlockState> planned = new HashMap<>();
|
||||
Set<Long> conflicts = new HashSet<>();
|
||||
eligible.sort(Comparator.comparingInt(SupportRequest::x)
|
||||
.thenComparingInt(SupportRequest::z)
|
||||
.thenComparingInt(SupportRequest::lowerMeetY)
|
||||
.thenComparingInt(SupportRequest::baseY));
|
||||
for (SupportRequest request : eligible) {
|
||||
MaterialKey materialKey = new MaterialKey(
|
||||
request.x(), request.z(), request.lowerMeetY());
|
||||
BlockState material = materials.get(materialKey);
|
||||
for (int y = request.lowerMeetY() + 1; y < request.baseY(); y++) {
|
||||
budget.consume(1);
|
||||
long positionKey = BlockPos.asLong(request.x(), y, request.z());
|
||||
if (conflicts.contains(positionKey)) {
|
||||
continue;
|
||||
}
|
||||
BlockState existing = planned.putIfAbsent(positionKey, material);
|
||||
if (existing != null && !existing.equals(material)) {
|
||||
planned.remove(positionKey);
|
||||
conflicts.add(positionKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
return planned;
|
||||
}
|
||||
|
||||
private static boolean gapIsClear(
|
||||
WorldGenLevel world, BoundingBox area, BlockPos.MutableBlockPos position,
|
||||
SupportRequest request, BatchCellBudget budget) {
|
||||
for (int y = request.lowerMeetY() + 1; y < request.baseY(); y++) {
|
||||
budget.consume(1);
|
||||
if (!area.isInside(position.set(request.x(), y, request.z()))) {
|
||||
return false;
|
||||
}
|
||||
BlockState state = world.getBlockState(position);
|
||||
if (state.isSolid() || NativeStructureVegetationClearer.isTreeBlock(state)
|
||||
|| !state.getFluidState().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static BlockState resolveSupportState(
|
||||
WorldGenLevel world, BlockPos.MutableBlockPos position,
|
||||
int x, int z, int lowerMeetY, BatchCellBudget budget) {
|
||||
budget.consume(1);
|
||||
BlockState below = world.getBlockState(position.set(x, lowerMeetY - 1, z));
|
||||
if (isTerrainSupport(below)) {
|
||||
return below;
|
||||
}
|
||||
return world.getBlockState(position.set(x, lowerMeetY, z));
|
||||
}
|
||||
|
||||
private static boolean isSolidBase(BlockState state) {
|
||||
return state != null && state.isSolid()
|
||||
&& !state.is(Blocks.STRUCTURE_VOID)
|
||||
&& !state.is(Blocks.JIGSAW)
|
||||
&& state.getFluidState().isEmpty();
|
||||
}
|
||||
|
||||
private static boolean isTerrainSupport(BlockState state) {
|
||||
return state.isSolid()
|
||||
&& !NativeStructureVegetationClearer.isTreeBlock(state)
|
||||
&& state.getFluidState().isEmpty();
|
||||
}
|
||||
|
||||
private static BlockPos referencePosition(StructureStart start) {
|
||||
BoundingBox bounds = start.getPieces().getFirst().getBoundingBox();
|
||||
BlockPos center = bounds.getCenter();
|
||||
return new BlockPos(center.getX(), bounds.minY(), center.getZ());
|
||||
}
|
||||
|
||||
private static long columnKey(int x, int z) {
|
||||
return (long) x << 32 ^ z & 0xffffffffL;
|
||||
}
|
||||
|
||||
private record RigidAnchor(
|
||||
PoolElementStructurePiece piece, BoundingBox bounds, int meetY) {
|
||||
}
|
||||
|
||||
private record LeafPlacement(
|
||||
StructurePlaceSettings settings,
|
||||
List<StructureTemplate.StructureBlockInfo> rawBlocks,
|
||||
StructureTemplate template) {
|
||||
}
|
||||
|
||||
private record OccupancyCell(BlockState state, boolean blocker) {
|
||||
}
|
||||
|
||||
private record LowestCell(
|
||||
int x, int y, int z, OccupancyCell occupancy) {
|
||||
}
|
||||
|
||||
private record SupportRequest(int x, int z, int lowerMeetY, int baseY) {
|
||||
}
|
||||
|
||||
private record MaterialKey(int x, int z, int lowerMeetY) {
|
||||
}
|
||||
|
||||
private static final class BatchCellBudget {
|
||||
private int consumed;
|
||||
|
||||
private void consume(int amount) {
|
||||
if (amount < 0 || consumed > MAX_BATCH_CELLS - amount) {
|
||||
throw BatchBudgetExceeded.INSTANCE;
|
||||
}
|
||||
consumed += amount;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class BatchBudgetExceeded extends RuntimeException {
|
||||
private static final BatchBudgetExceeded INSTANCE = new BatchBudgetExceeded();
|
||||
|
||||
private BatchBudgetExceeded() {
|
||||
super(null, null, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -114,7 +114,6 @@ public final class NativeStructureTerrainIntegrator {
|
||||
return;
|
||||
}
|
||||
if (mode == IrisStructureTerrainMode.VACUUM) {
|
||||
carvePieceBoxes(world, area, start, terrain);
|
||||
return;
|
||||
}
|
||||
if (mode == IrisStructureTerrainMode.ENCASE) {
|
||||
@@ -267,6 +266,7 @@ public final class NativeStructureTerrainIntegrator {
|
||||
}
|
||||
IrisStructureTerrainMode mode = terrain.resolvedMode();
|
||||
return mode == IrisStructureTerrainMode.ENCASE
|
||||
|| mode == IrisStructureTerrainMode.VACUUM
|
||||
|| mode == IrisStructureTerrainMode.SOURCE
|
||||
&& start.getStructure().terrainAdaptation() != TerrainAdjustment.NONE;
|
||||
}
|
||||
|
||||
+1
-1
@@ -360,7 +360,7 @@ final class ModdedNativeStructureStage {
|
||||
world, area, terrainTargets, this::resolvePaletteBlock);
|
||||
} catch (Throwable error) {
|
||||
throw NativeStructureGenerationException.failure(
|
||||
"terrain carving", nativeStructureBatchContext(placementGroups),
|
||||
"terrain preparation", nativeStructureBatchContext(placementGroups),
|
||||
chunkPos.x(), chunkPos.z(), error);
|
||||
}
|
||||
for (NativePlacementGroup group : placementGroups) {
|
||||
|
||||
+2
@@ -63,6 +63,8 @@ public class NativeStructureFailureContractTest {
|
||||
String placement = source.substring(placementStart, placementEnd);
|
||||
|
||||
assertTrue(placement.contains("\"terrain integration\""));
|
||||
assertTrue(placement.contains("\"terrain preparation\""));
|
||||
assertFalse(placement.contains("\"terrain carving\""));
|
||||
assertTrue(placement.contains("prepareSurfaceStructures"));
|
||||
assertTrue(placement.contains("clearIntersectingVegetation"));
|
||||
assertTrue(placement.indexOf("clearIntersectingVegetation")
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package art.arcane.iris.nativegen;
|
||||
|
||||
import art.arcane.iris.engine.object.IrisStructureTerrain;
|
||||
import art.arcane.iris.engine.object.IrisStructureTerrainMode;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.TerrainAdjustment;
|
||||
import net.minecraft.world.level.levelgen.structure.pieces.PiecesContainer;
|
||||
import net.minecraft.world.level.levelgen.structure.structures.DesertPyramidPiece;
|
||||
import net.minecraft.world.level.levelgen.structure.structures.DesertPyramidStructure;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class NativeStructureVacuumParityTest {
|
||||
@BeforeClass
|
||||
public static void bootstrapMinecraftRegistries() {
|
||||
SharedConstants.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitVacuumUsesSharedThinSurfaceFittingOnModdedLoaders() {
|
||||
Structure structure = new DesertPyramidStructure(
|
||||
new Structure.StructureSettings(
|
||||
HolderSet.empty(), Map.of(),
|
||||
GenerationStep.Decoration.SURFACE_STRUCTURES, TerrainAdjustment.NONE));
|
||||
DesertPyramidPiece piece = new DesertPyramidPiece(RandomSource.create(7L), 0, 0);
|
||||
StructureStart start = new StructureStart(
|
||||
structure, new ChunkPos(0, 0), 0, new PiecesContainer(List.of(piece)));
|
||||
NativeStructureTerrainIntegrator.TerrainTarget target =
|
||||
new NativeStructureTerrainIntegrator.TerrainTarget(
|
||||
"test:vacuum", start,
|
||||
new IrisStructureTerrain().setMode(IrisStructureTerrainMode.VACUUM));
|
||||
|
||||
assertTrue(NativeStructureSurfaceFitter.requiresSurfaceTerrain(target));
|
||||
assertEquals(TerrainAdjustment.BEARD_THIN,
|
||||
NativeStructureSurfaceFitter.effectiveSurfaceAdjustment(target));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user