mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-25 21:40:05 +00:00
Production-readiness suite fixes
Surface hydrology policy split, native terrain height cache, watched-edit hotload cancellation, world-save requestSave with tracked ownership flush, background-task drain that follows saves admitted mid-drain, peaceful spawn guard, cached-completion terminal state, IO drains off the owner thread. Studio pregen at scale: detached historical runtimes keep the configured noise cache instead of the Studio-wide cache; retired runtimes are no longer pinned by the volumetric fragment filter's per-thread scratch; pregen mantle cleanup and native region flushes run on per-run threads instead of the server thread and the shared Iris IO pool (the flush on the IO pool starved tectonic plate loads and wedged every Paper worker in Mantle.getChunks); background-task cancellation claims tasks atomically.
This commit is contained in:
1 parent
4de1aea8b9
commit
62e61970f8
150 files changed
+7892
-1341
No files matched your search
+17
-3
@@ -19,6 +19,7 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import art.arcane.iris.nativegen.NativeTransitionColumn;
|
||||
import art.arcane.iris.nativegen.NativeTerrainHeightCache;
|
||||
import art.arcane.iris.engine.history.TerrainBoundarySignature;
|
||||
import art.arcane.iris.engine.history.NativeBiomeSpawnSelection;
|
||||
import java.util.Optional;
|
||||
@@ -107,6 +108,7 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.CancellationException;
|
||||
@@ -145,6 +147,7 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
|
||||
private final ModdedNativeStructureStage nativeStructures = new ModdedNativeStructureStage(this);
|
||||
private final ModdedSpawnTableMerger spawnTables = new ModdedSpawnTableMerger(this);
|
||||
private final ModdedImportedFeatureStage importedFeatures;
|
||||
private final NativeTerrainHeightCache terrainHeights = new NativeTerrainHeightCache();
|
||||
private final AtomicBoolean announced = new AtomicBoolean(false);
|
||||
private final IntConsumer generationRuntimeRetirementListener = this::retireGenerationRuntimeCaches;
|
||||
private volatile boolean unloading;
|
||||
@@ -1381,15 +1384,18 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
|
||||
@Override
|
||||
public int getBaseHeight(int x, int z, Heightmap.Types type, LevelHeightAccessor heightAccessor, RandomState randomState) {
|
||||
Engine current = requireDataQueryEngine("base height");
|
||||
boolean ignoreFluid = !type.isOpaque().test(Blocks.WATER.defaultBlockState());
|
||||
try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope(
|
||||
current, x, z, "modded_base_height");
|
||||
GenerationSessionLease lease = current.acquireGenerationLease("modded_base_height");
|
||||
IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) {
|
||||
Optional<TerrainBoundarySignature> resolved = current.getComplex().resolvedTerrainColumn(x, z);
|
||||
NativeTerrainHeightCache.Query query = new NativeTerrainHeightCache.Query(
|
||||
current.getCacheID(), x, z, type, heightAccessor.getMinY(), heightAccessor.getHeight());
|
||||
OptionalInt resolved = terrainHeights.resolvedHeight(query,
|
||||
() -> resolvedBaseHeight(current, x, z, type, heightAccessor));
|
||||
if (resolved.isPresent()) {
|
||||
return NativeTransitionColumn.height(resolved.get(), type, heightAccessor);
|
||||
return resolved.getAsInt();
|
||||
}
|
||||
boolean ignoreFluid = !type.isOpaque().test(Blocks.WATER.defaultBlockState());
|
||||
int height = current.getDimensionStackContext() == null
|
||||
? current.getHeight(x, z, ignoreFluid)
|
||||
: Engine.hostHeight(current, x, z, ignoreFluid);
|
||||
@@ -1399,6 +1405,13 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private OptionalInt resolvedBaseHeight(Engine current, int x, int z, Heightmap.Types type, LevelHeightAccessor heightAccessor) {
|
||||
Optional<TerrainBoundarySignature> resolved = current.getComplex().resolvedTerrainColumn(x, z);
|
||||
return resolved.isPresent()
|
||||
? OptionalInt.of(NativeTransitionColumn.height(resolved.get(), type, heightAccessor))
|
||||
: OptionalInt.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NoiseColumn getBaseColumn(int x, int z, LevelHeightAccessor heightAccessor, RandomState randomState) {
|
||||
int minY = heightAccessor.getMinY();
|
||||
@@ -1482,6 +1495,7 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
|
||||
}
|
||||
|
||||
private void retireGenerationRuntimeCaches(int runtimeIdentity) {
|
||||
terrainHeights.evictRuntime(runtimeIdentity);
|
||||
structureBiomeSource.evictRuntime(runtimeIdentity);
|
||||
importedFeatures.evictRuntime(runtimeIdentity);
|
||||
spawnTables.evictRuntime(runtimeIdentity);
|
||||
|
||||
+9
-1
@@ -46,6 +46,7 @@ import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntitySpawnReason;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
@@ -120,13 +121,20 @@ public final class ModdedEntitySpawner {
|
||||
return null;
|
||||
}
|
||||
|
||||
Entity entity = type.spawn(level, BlockPos.containing(x, y, z), reasonFor(irisEntity.getReason()));
|
||||
Entity entity = spawnNative(type, level, BlockPos.containing(x, y, z), reasonFor(irisEntity.getReason()));
|
||||
if (entity != null) {
|
||||
entity.snapTo(x, y, z, 0F, 0F);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
static Entity spawnNative(EntityType<?> type, ServerLevel level, BlockPos position, EntitySpawnReason reason) {
|
||||
if (level.getDifficulty() == Difficulty.PEACEFUL && !type.isAllowedInPeaceful()) {
|
||||
return null;
|
||||
}
|
||||
return type.spawn(level, position, reason);
|
||||
}
|
||||
|
||||
private static void applyConfig(Engine engine, IrisEntity irisEntity, Entity entity, ServerLevel level, int blockX, int blockY, int blockZ, RNG rng, boolean riseEffectActive) {
|
||||
String customName = irisEntity.getCustomName();
|
||||
if (customName != null) {
|
||||
|
||||
+5
-7
@@ -398,10 +398,7 @@ final class ModdedNativeStructureStage {
|
||||
}
|
||||
IrisStaticObjectLayer staticObjects = current.getDimension().getStaticObjectLayer(current.getData());
|
||||
Predicate<BlockPos> protectedPosition = nativeStructureProtection(current, staticObjects);
|
||||
WorldGenLevel boundedWorld = staticObjects.isEmpty()
|
||||
&& current.getDimensionStackContext() == null
|
||||
? world
|
||||
: ModdedNativeStructureWorldgenAccess.create(
|
||||
WorldGenLevel boundedWorld = ModdedNativeStructureWorldgenAccess.create(
|
||||
world, chunkPos, worldgenSurfaceHeight(current, world.getMinY()), worldgenFloorHeight(current, world.getMinY()),
|
||||
current.getDimensionStackContext() != null,
|
||||
protectedPosition);
|
||||
@@ -413,11 +410,12 @@ final class ModdedNativeStructureStage {
|
||||
"vegetation cleanup", nativeStructureBatchContext(placementGroups),
|
||||
chunkPos.x(), chunkPos.z(), error);
|
||||
}
|
||||
NativeStructureSurfaceFitter.VacuumFoundationPlan vacuumFoundationPlan;
|
||||
NativeStructureSurfaceFitter.SurfaceTerrainPlan surfaceTerrainPlan;
|
||||
try {
|
||||
vacuumFoundationPlan = NativeStructureSurfaceFitter.prepareSurfaceStructures(
|
||||
surfaceTerrainPlan = NativeStructureSurfaceFitter.prepareSurfaceStructures(
|
||||
boundedWorld, area, terrainTargets,
|
||||
(x, z) -> Engine.hostHeight(current, x, z, true) + current.getMinHeight());
|
||||
surfaceTerrainPlan.primeHeightmaps(chunk);
|
||||
} catch (Throwable error) {
|
||||
throw NativeStructureGenerationException.failure(
|
||||
"terrain integration", nativeStructureBatchContext(placementGroups),
|
||||
@@ -445,7 +443,7 @@ final class ModdedNativeStructureStage {
|
||||
}
|
||||
try {
|
||||
NativeStructureSurfaceFitter.repairVacuumFoundations(
|
||||
boundedWorld, area, vacuumFoundationPlan);
|
||||
boundedWorld, area, surfaceTerrainPlan);
|
||||
} catch (Throwable error) {
|
||||
throw NativeStructureGenerationException.failure(
|
||||
"foundation repair", nativeStructureBatchContext(placementGroups),
|
||||
|
||||
@@ -210,7 +210,7 @@ public final class ModdedPlatform implements IrisPlatform {
|
||||
}
|
||||
EntityType<?> type = (EntityType<?>) resolved.nativeHandle();
|
||||
BlockPos pos = BlockPos.containing(x, y, z);
|
||||
Entity entity = type.spawn(level, pos, EntitySpawnReason.COMMAND);
|
||||
Entity entity = ModdedEntitySpawner.spawnNative(type, level, pos, EntitySpawnReason.COMMAND);
|
||||
return entity != null;
|
||||
}
|
||||
|
||||
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.object.IrisEntity;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntitySpawnReason;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ModdedEntitySpawnDifficultyTest {
|
||||
@BeforeClass
|
||||
public static void bootstrapMinecraftRegistries() {
|
||||
SharedConstants.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sharedPlatformSpawnReturnsFalseForPeacefulHostiles() {
|
||||
ServerLevel level = mock(ServerLevel.class);
|
||||
when(level.getDifficulty()).thenReturn(Difficulty.PEACEFUL);
|
||||
ModdedPlatform platform = new ModdedPlatform(mock(ModdedLoader.class));
|
||||
|
||||
assertFalse(platform.spawnEntity(new ModdedPlatformWorld(level), "minecraft:zombie", 0.5, 80, 0.5));
|
||||
|
||||
verify(level, never()).addFreshEntity(any(Entity.class));
|
||||
verify(level, never()).enabledFeatures();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ambientSpawnReturnsNullForPeacefulHostilesBeforeApplyingConfiguration() {
|
||||
ServerLevel level = mock(ServerLevel.class);
|
||||
ServerChunkCache chunks = mock(ServerChunkCache.class);
|
||||
when(level.getDifficulty()).thenReturn(Difficulty.PEACEFUL);
|
||||
when(level.getChunkSource()).thenReturn(chunks);
|
||||
when(chunks.getChunkNow(anyInt(), anyInt())).thenReturn(mock(LevelChunk.class));
|
||||
IrisEntity configured = new IrisEntity().setType("zombie").setSpawnEffectRiseOutOfGround(false);
|
||||
|
||||
assertNull(ModdedEntitySpawner.spawn(mock(Engine.class), configured, level, 0, 80, 0, new RNG(1L)));
|
||||
|
||||
verify(level, never()).addFreshEntity(any(Entity.class));
|
||||
verify(level, never()).enabledFeatures();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void peacefulRejectsEveryNativeForbiddenTypeBeforeCreation() {
|
||||
ServerLevel level = mock(ServerLevel.class);
|
||||
when(level.getDifficulty()).thenReturn(Difficulty.PEACEFUL);
|
||||
int rejected = 0;
|
||||
|
||||
for (EntityType<?> registered : BuiltInRegistries.ENTITY_TYPE) {
|
||||
if (registered.isAllowedInPeaceful()) {
|
||||
continue;
|
||||
}
|
||||
EntityType<?> type = spy(registered);
|
||||
assertNull(ModdedEntitySpawner.spawnNative(type, level, BlockPos.ZERO, EntitySpawnReason.NATURAL));
|
||||
verify(type, never()).spawn(level, BlockPos.ZERO, EntitySpawnReason.NATURAL);
|
||||
rejected++;
|
||||
}
|
||||
|
||||
assertTrue(rejected > 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void peacefulPreservesPassiveAndNativeMonsterCategoryExceptions() {
|
||||
ServerLevel level = mock(ServerLevel.class);
|
||||
when(level.getDifficulty()).thenReturn(Difficulty.PEACEFUL);
|
||||
Entity created = mock(Entity.class);
|
||||
|
||||
for (String key : List.of("cow", "villager", "wolf", "piglin", "shulker")) {
|
||||
EntityType<?> type = spy(BuiltInRegistries.ENTITY_TYPE.getValue(Identifier.parse("minecraft:" + key)));
|
||||
doReturn(created).when(type).spawn(level, BlockPos.ZERO, EntitySpawnReason.NATURAL);
|
||||
|
||||
assertSame(key, created, ModdedEntitySpawner.spawnNative(type, level, BlockPos.ZERO, EntitySpawnReason.NATURAL));
|
||||
verify(type).spawn(level, BlockPos.ZERO, EntitySpawnReason.NATURAL);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonPeacefulDifficultiesPreserveHostileSpawns() {
|
||||
ServerLevel level = mock(ServerLevel.class);
|
||||
Entity created = mock(Entity.class);
|
||||
|
||||
for (Difficulty difficulty : List.of(Difficulty.EASY, Difficulty.NORMAL, Difficulty.HARD)) {
|
||||
EntityType<?> type = spy(BuiltInRegistries.ENTITY_TYPE.getValue(Identifier.parse("minecraft:zombie")));
|
||||
when(level.getDifficulty()).thenReturn(difficulty);
|
||||
doReturn(created).when(type).spawn(level, BlockPos.ZERO, EntitySpawnReason.NATURAL);
|
||||
|
||||
assertSame(created, ModdedEntitySpawner.spawnNative(type, level, BlockPos.ZERO, EntitySpawnReason.NATURAL));
|
||||
verify(type).spawn(level, BlockPos.ZERO, EntitySpawnReason.NATURAL);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unrelatedNativeSpawnFailuresStillPropagate() {
|
||||
ServerLevel level = mock(ServerLevel.class);
|
||||
when(level.getDifficulty()).thenReturn(Difficulty.PEACEFUL);
|
||||
EntityType<?> type = spy(BuiltInRegistries.ENTITY_TYPE.getValue(Identifier.parse("minecraft:cow")));
|
||||
IllegalStateException failure = new IllegalStateException("Spawn rejected by platform");
|
||||
doThrow(failure).when(type).spawn(level, BlockPos.ZERO, EntitySpawnReason.NATURAL);
|
||||
|
||||
assertSame(failure, assertThrows(IllegalStateException.class,
|
||||
() -> ModdedEntitySpawner.spawnNative(type, level, BlockPos.ZERO, EntitySpawnReason.NATURAL)));
|
||||
}
|
||||
}
|
||||
+91
@@ -1,9 +1,14 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import art.arcane.iris.engine.object.IrisStructureTerrain;
|
||||
import art.arcane.iris.engine.object.IrisStructureTerrainMode;
|
||||
import art.arcane.iris.nativegen.NativeStructurePostProcessor;
|
||||
import art.arcane.iris.nativegen.NativeStructureTerrainIntegrator;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
@@ -12,10 +17,25 @@ import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeManager;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||
import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece;
|
||||
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.pools.StructurePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
|
||||
import net.minecraft.world.level.levelgen.structure.structures.DesertPyramidStructure;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.GravityProcessor;
|
||||
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.templatesystem.StructureTemplate;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
@@ -30,11 +50,13 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedNativeStructureWorldgenAccessTest {
|
||||
@@ -49,6 +71,69 @@ public class ModdedNativeStructureWorldgenAccessTest {
|
||||
Bootstrap.bootStrap();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ordinaryWorldFlattenPreparationHandlesReferencedVillagePiecesBeyondEightChunks() {
|
||||
int farX = (GENERATION_CENTER_X - 9) << 4;
|
||||
int farZ = (GENERATION_CENTER_Z + 4) << 4;
|
||||
BoundingBox bounds = new BoundingBox(farX, 64, farZ, farX + 4, 90, farZ + 4);
|
||||
PoolElementStructurePiece piece = new PoolElementStructurePiece(
|
||||
null, StructurePoolElement.single("minecraft:empty").apply(StructureTemplatePool.Projection.RIGID),
|
||||
new BlockPos(farX, 64, farZ), 1, Rotation.NONE, bounds, LiquidSettings.APPLY_WATERLOGGING);
|
||||
Structure structure = new DesertPyramidStructure(new Structure.StructureSettings(
|
||||
HolderSet.empty(), Map.of(), GenerationStep.Decoration.SURFACE_STRUCTURES, TerrainAdjustment.BEARD_BOX));
|
||||
StructureStart start = new StructureStart(structure, generationCenter(), 0, new PiecesContainer(List.of(piece)));
|
||||
List<NativeStructureTerrainIntegrator.TerrainTarget> targets = List.of(
|
||||
new NativeStructureTerrainIntegrator.TerrainTarget("minecraft:village_plains", start,
|
||||
new IrisStructureTerrain().setMode(IrisStructureTerrainMode.FLATTEN)));
|
||||
BoundingBox area = new BoundingBox(generationCenter().getMinBlockX(), -64, generationCenter().getMinBlockZ(),
|
||||
generationCenter().getMaxBlockX(), 319, generationCenter().getMaxBlockZ());
|
||||
RecordingDelegate unbounded = new RecordingDelegate();
|
||||
unbounded.rejectDistantTerrainReads = true;
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> NativeStructurePostProcessor.prepareTerrain(
|
||||
unbounded.world(), area, targets, (palette, rng, x, y, z) -> Blocks.STONE.defaultBlockState()));
|
||||
|
||||
RecordingDelegate recording = new RecordingDelegate();
|
||||
recording.rejectDistantTerrainReads = true;
|
||||
ModdedNativeStructureWorldgenAccess access = access(recording);
|
||||
NativeStructurePostProcessor.prepareTerrain(
|
||||
access, area, targets, (palette, rng, x, y, z) -> Blocks.STONE.defaultBlockState());
|
||||
|
||||
assertEquals(0, recording.heightReads);
|
||||
assertEquals(0, recording.chunkReads);
|
||||
assertEquals(0, recording.terrainReads);
|
||||
assertEquals(0, recording.mutations);
|
||||
assertEquals(91, access.getHeight(Heightmap.Types.WORLD_SURFACE_WG,
|
||||
generationCenter().getMinBlockX(), generationCenter().getMinBlockZ()));
|
||||
assertEquals(1, recording.heightReads);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectedTemplatePrefiltersUseRemoteTerrainAndRetainLocalHeightmaps() {
|
||||
RecordingDelegate recording = new RecordingDelegate();
|
||||
recording.rejectDistantTerrainReads = true;
|
||||
ModdedNativeStructureWorldgenAccess access = access(recording);
|
||||
GravityProcessor gravity = new GravityProcessor(Heightmap.Types.WORLD_SURFACE_WG, -1);
|
||||
BlockPos remote = new BlockPos((GENERATION_CENTER_X - 9) << 4, 64, (GENERATION_CENTER_Z + 4) << 4);
|
||||
StructureTemplate.StructureBlockInfo raw = new StructureTemplate.StructureBlockInfo(
|
||||
new BlockPos(0, 4, 0), Blocks.DIRT_PATH.defaultBlockState(), null);
|
||||
StructurePlaceSettings settings = new StructurePlaceSettings().addProcessor(gravity);
|
||||
|
||||
StructureTemplate.StructureBlockInfo projected = StructureTemplate.processBlockInfos(
|
||||
access, remote, BlockPos.ZERO, settings, List.of(raw)).getFirst();
|
||||
|
||||
assertEquals(remote.atY(SURFACE_FIRST_FREE_Y + 3), projected.pos());
|
||||
assertEquals(0, recording.heightReads);
|
||||
BlockPos local = generationCenter().getMiddleBlockPosition(64);
|
||||
StructureTemplate.StructureBlockInfo localBlock = new StructureTemplate.StructureBlockInfo(
|
||||
BlockPos.ZERO, Blocks.DIRT_PATH.defaultBlockState(), null);
|
||||
StructureTemplate.StructureBlockInfo localProjection = StructureTemplate.processBlockInfos(
|
||||
access, local, BlockPos.ZERO, settings, List.of(localBlock)).getFirst();
|
||||
assertEquals(local.atY(90), localProjection.pos());
|
||||
assertEquals(1, recording.heightReads);
|
||||
assertEquals(0, recording.chunkReads);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distanceTwoTerrainReadsNeverReachTheDelegate() {
|
||||
RecordingDelegate recording = new RecordingDelegate();
|
||||
@@ -253,6 +338,7 @@ public class ModdedNativeStructureWorldgenAccessTest {
|
||||
private int statusChunkReads;
|
||||
private int mutations;
|
||||
private int events;
|
||||
private boolean rejectDistantTerrainReads;
|
||||
|
||||
private RecordingDelegate() {
|
||||
this.biome = Holder.direct((Biome) null);
|
||||
@@ -293,6 +379,11 @@ public class ModdedNativeStructureWorldgenAccessTest {
|
||||
return 384;
|
||||
}
|
||||
if (name.equals("getHeight")) {
|
||||
if (rejectDistantTerrainReads
|
||||
&& (Math.abs(((int) arguments[1] >> 4) - GENERATION_CENTER_X) > 1
|
||||
|| Math.abs(((int) arguments[2] >> 4) - GENERATION_CENTER_Z) > 1)) {
|
||||
throw new IllegalStateException("Requested chunk unavailable during world generation");
|
||||
}
|
||||
heightReads++;
|
||||
return 91;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user