Take chunk uses through pins

- Every getChunk(..).use() site calls Mantle.useChunk, which cannot fail because a save closed the chunk in between
- Covered-chunk cleanup, custom block and static object layers take their chunk through useChunk
- Discovered flag writes go through Mantle.flag instead of an unpinned chunk
- Test mantles stub useChunk the way the real one behaves
This commit is contained in:
Brian Neumann-Fopiano
2026-09-28 15:46:54 -04:00
parent 8899c9e3f9
commit 59165df427
42 changed files with 94 additions and 86 deletions
@@ -245,7 +245,7 @@ public final class ModdedWorldManager implements EngineWorldManager {
return true;
}
MantleChunk<Matter> chunk = mantle.getChunk(chunkX, chunkZ).use();
MantleChunk<Matter> chunk = mantle.useChunk(chunkX, chunkZ);
try {
List<PreparedMarkerSpawn> markers = markerSystemEnabled()
? prepareMarkerSpawns(level, chunkX, chunkZ, chunk) : List.of();
@@ -380,7 +380,7 @@ public final class ModdedWorldManager implements EngineWorldManager {
return;
}
MantleChunk<Matter> chunk = mantle.getChunk(chunkX, chunkZ).use();
MantleChunk<Matter> chunk = mantle.useChunk(chunkX, chunkZ);
try {
if (markerSystemEnabled()) {
spawnPreparedMarkers(level, prepareMarkerSpawns(level, chunkX, chunkZ, chunk), false);
@@ -188,7 +188,7 @@ public final class ModdedChunkUpdateService implements ModdedTickableService {
return;
}
MantleChunk<Matter> chunk = mantle.getChunk(chunkX, chunkZ).use();
MantleChunk<Matter> chunk = mantle.useChunk(chunkX, chunkZ);
try {
chunk.raiseFlagUnchecked(MantleFlag.ETCHED, () -> {
chunk.raiseFlagUnchecked(MantleFlag.TILE, () -> runTilePass(engine, level, chunkX, chunkZ, chunk));
@@ -70,8 +70,7 @@ public class ModdedSavedBiomeConsumersTest {
when(engine.getWorld()).thenReturn(mock(IrisWorld.class));
when(engineMantle.getMantle()).thenReturn(mantle);
when(mantle.isChunkLoaded(2, -1)).thenReturn(true);
when(mantle.getChunk(2, -1)).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(2, -1)).thenReturn(chunk);
BiomeEnvironment environment = environment();
when(engine.getSurfaceBiomeEnvironment(40, -8))
.thenThrow(new SavedBiomeUnavailableException("Loading", true))
@@ -212,7 +212,7 @@ public interface EngineMantle extends MatterGenerator {
boolean protectUpper = upperCtx != null;
DimensionStackContext stackContext = getEngine().getDimensionStackContext();
MantleChunk<Matter> chunk = getMantle().getChunk(x, z).use();
MantleChunk<Matter> chunk = getMantle().useChunk(x, z);
try {
if (stackContext != null) {
DimensionStackLayout[] layouts = new DimensionStackLayout[256];
@@ -317,24 +317,33 @@ public interface EngineMantle extends MatterGenerator {
}
default boolean cleanupCoveredChunk(int x, int z, boolean force) {
return cleanupCoveredChunk(getMantle().getChunk(x, z), force);
MantleChunk<Matter> chunk = getMantle().useChunk(x, z);
try {
return cleanupUsedChunk(chunk, force);
} finally {
chunk.release();
}
}
default boolean cleanupCoveredChunk(MantleChunk<Matter> chunk, boolean force) {
chunk.use();
try {
synchronized (chunk) {
if (chunk.isFlagged(MantleFlag.CLEANED)) {
return false;
}
chunk.raiseFlagUnchecked(MantleFlag.CLEANED, () -> cleanupSlices(chunk, force));
return true;
}
return cleanupUsedChunk(chunk, force);
} finally {
chunk.release();
}
}
private boolean cleanupUsedChunk(MantleChunk<Matter> chunk, boolean force) {
synchronized (chunk) {
if (chunk.isFlagged(MantleFlag.CLEANED)) {
return false;
}
chunk.raiseFlagUnchecked(MantleFlag.CLEANED, () -> cleanupSlices(chunk, force));
return true;
}
}
private void cleanupSlices(MantleChunk<Matter> chunk, boolean force) {
MantleSliceRetention.deleteUnlessRetained(chunk, NativeBlockState.class);
if (force) {
@@ -880,7 +880,7 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable {
while (chunk == null) {
// Losing this race must release our own use, never the winner's: the winner is already
// writing through the chunk it published.
MantleChunk<Matter> acquired = mantle.getChunk(cx, cz).use();
MantleChunk<Matter> acquired = mantle.useChunk(cx, cz);
if (window.compareAndSet(index, null, acquired)) {
chunk = acquired;
} else {
@@ -26,7 +26,7 @@ public final class ObjectContinuationPersistence {
List<ObjectContinuationBundle.ChunkPosition> touched = List.copyOf(destinations.keySet());
for (Map.Entry<ObjectContinuationBundle.ChunkPosition, Supplier<Matter>> entry : destinations.entrySet()) {
ObjectContinuationBundle.ChunkPosition destination = entry.getKey();
MantleChunk<Matter> chunk = mantle.getChunk(destination.x(), destination.z()).use();
MantleChunk<Matter> chunk = mantle.useChunk(destination.x(), destination.z());
try {
synchronized (chunk) {
if (chunk.isFlagged(MantleFlag.REAL)) {
@@ -371,7 +371,7 @@ public interface Engine extends DataProvider, Fallible, BlockUpdater, Renderer,
Mantle<Matter> mantle = getMantle().getMantle();
int chunkX = x >> 4;
int chunkZ = z >> 4;
return mantle.hasTectonicPlate(chunkX >> 5, chunkZ >> 5) ? mantle.getChunk(chunkX, chunkZ).use() : null;
return mantle.hasTectonicPlate(chunkX >> 5, chunkZ >> 5) ? mantle.useChunk(chunkX, chunkZ) : null;
}
/**
@@ -763,7 +763,7 @@ public interface Engine extends DataProvider, Fallible, BlockUpdater, Renderer,
return o.getObject().getLoadKey() + "@" + o.getId();
}
MantleChunk<Matter> chunk = getMantle().getMantle().getChunk(x >> 4, z >> 4).use();
MantleChunk<Matter> chunk = getMantle().getMantle().useChunk(x >> 4, z >> 4);
try {
String raw = chunk.get(x & 15, y, z & 15, String.class);
return (raw == null || raw.isEmpty()) ? null : raw;
@@ -773,7 +773,7 @@ public interface Engine extends DataProvider, Fallible, BlockUpdater, Renderer,
}
default PlacedObject getObjectPlacement(int x, int y, int z) {
MantleChunk<Matter> chunk = getMantle().getMantle().getChunk(x >> 4, z >> 4).use();
MantleChunk<Matter> chunk = getMantle().getMantle().useChunk(x >> 4, z >> 4);
try {
return getObjectPlacement(x, y, z, chunk);
} finally {
@@ -262,7 +262,7 @@ public interface EngineMode extends Staged {
}
private void restoreObjectMetadata(int x, int z, Matter continuation) {
MantleChunk<Matter> chunk = getMantle().getMantle().getChunk(x >> 4, z >> 4).use();
MantleChunk<Matter> chunk = getMantle().getMantle().useChunk(x >> 4, z >> 4);
try {
synchronized (chunk) {
MatterSlice<NativeBlockState> states = continuation.getSlice(NativeBlockState.class);
@@ -1507,7 +1507,7 @@ public class IrisEngine implements Engine {
boolean skipRealFlag = platformHooks.shouldBypassMantleStages(this);
if (!skipRealFlag) {
MantleChunk<Matter> chunk = getMantle().getMantle().getChunk(x >> 4, z >> 4).use();
MantleChunk<Matter> chunk = getMantle().getMantle().useChunk(x >> 4, z >> 4);
try {
synchronized (chunk) {
Matter section = chunk.get(0);
@@ -157,7 +157,7 @@ final class WorldChunkMaintenance {
}
if (!J.isFolia()) {
manager.getMantle().getChunk(chunkX, chunkZ).flag(MantleFlag.DISCOVERED, true);
manager.getMantle().flag(chunkX, chunkZ, MantleFlag.DISCOVERED, true);
return;
}
@@ -473,7 +473,7 @@ final class WorldChunkMaintenance {
preparing = true;
}
opened = router.openSavedChunkMantle(target.x(), target.z());
loaded = opened.mantle().getChunk(target.x(), target.z()).use();
loaded = opened.mantle().useChunk(target.x(), target.z());
opened.detachThread();
synchronized (this) {
if (cancelled) {
@@ -124,7 +124,7 @@ public class IrisCarveModifier extends EngineAssignedModifier<NativeBlockState>
}
}
MantleChunk<Matter> mantleChunk = mantle.getChunk(x, z).use();
MantleChunk<Matter> mantleChunk = mantle.useChunk(x, z);
try {
PrecisionStopwatch resolveStopwatch = PrecisionStopwatch.start();
CaveTerrainSnapshot terrain = CaveTerrainSnapshot.capture(mantleChunk, x, z);
@@ -873,7 +873,7 @@ public class IrisCarveModifier extends EngineAssignedModifier<NativeBlockState>
context.setCaveTerrain(null);
}
if (terrain == null || !terrain.covers(blockX >> 4, blockZ >> 4)) {
MantleChunk<Matter> chunk = mantle.getChunk(blockX >> 4, blockZ >> 4).use();
MantleChunk<Matter> chunk = mantle.useChunk(blockX >> 4, blockZ >> 4);
try {
terrain = CaveTerrainSnapshot.capture(chunk, blockX >> 4, blockZ >> 4);
} finally {
@@ -19,11 +19,11 @@ public class IrisCustomModifier extends EngineAssignedModifier<NativeBlockState>
@Override
public void onModify(int x, int z, Hunk<NativeBlockState> output, boolean multicore, ChunkContext context) {
MantleChunk<Matter> mc = getEngine().getMantle().getMantle().getChunk(x >> 4, z >> 4);
MantleChunk<Matter> mc = getEngine().getMantle().getMantle().useChunk(x >> 4, z >> 4);
if (!mc.isFlagged(MantleFlag.CUSTOM_ACTIVE)) {
mc.release();
return;
}
mc.use();
BurstExecutor burst = MultiBurst.burst.burst(output.getHeight());
burst.setMulticore(multicore);
@@ -93,7 +93,7 @@ public class IrisDepositModifier extends EngineAssignedModifier<NativeBlockState
&& irisEngine.hasGenerationRuntimeScope() ? irisEngine : null;
PreparationContext preparation = new PreparationContext(context, generationEngine,
generationEngine == null ? null : generationEngine.captureGenerationRuntimeBinding(), new Throwable[PREPARATION_BATCH_COUNT]);
MantleChunk chunk = getEngine().getMantle().getMantle().getChunk(x, z).use();
MantleChunk chunk = getEngine().getMantle().getMantle().useChunk(x, z);
Throwable callerFailure = null;
try {
for (int i = 0; i < generators.size(); i++) {
@@ -82,7 +82,7 @@ public final class IrisDimensionStackActuator extends EngineAssignedActuator<Nat
if (getEngine().getDimensionStackContext() == null) {
return;
}
MantleChunk<Matter> mantleChunk = getEngine().getMantle().getMantle().getChunk(x >> 4, z >> 4).use();
MantleChunk<Matter> mantleChunk = getEngine().getMantle().getMantle().useChunk(x >> 4, z >> 4);
try {
MetadataCleaner metadata = new MetadataCleaner(mantleChunk, blocks.getHeight());
for (int localX = 0; localX < blocks.getWidth(); localX++) {
@@ -111,7 +111,7 @@ public final class IrisDimensionStackActuator extends EngineAssignedActuator<Nat
return;
}
context.setCaveTerrain(null);
MantleChunk<Matter> chunk = engine.getMantle().getMantle().getChunk(x >> 4, z >> 4).use();
MantleChunk<Matter> chunk = engine.getMantle().getMantle().useChunk(x >> 4, z >> 4);
try {
synchronized (chunk) {
clearNaturalMetadata(chunk, context, height);
@@ -132,7 +132,7 @@ public final class EngineBukkitOps {
return;
}
MantleChunk<Matter> chunk = mantle.getChunk(c).use();
MantleChunk<Matter> chunk = mantle.useChunk(c);
try {
Runnable tileTask = () -> materializeTiles(engine, c, chunk);
Runnable customTask = () -> materializeCustomBlocks(engine, c, chunk);
@@ -461,7 +461,7 @@ public final class EngineBukkitOps {
}
public static KList<IrisLootTable> getLootTables(Engine engine, RNG rng, Block b) {
MantleChunk<Matter> mc = engine.getMantle().getMantle().getChunk(b.getChunk()).use();
MantleChunk<Matter> mc = engine.getMantle().getMantle().useChunk(b.getChunk());
try {
return getLootTables(engine, rng, b, mc, hasNativeLootTable(b));
} finally {
@@ -264,7 +264,7 @@ public final class NativeStructureOwnershipStore {
@Override
public NativeStructureOwnershipBundle read(int chunkX, int chunkZ) {
RuntimeMantleOwner owner = owner();
MantleChunk<Matter> chunk = owner.mantle().getMantle().getChunk(chunkX, chunkZ).use();
MantleChunk<Matter> chunk = owner.mantle().getMantle().useChunk(chunkX, chunkZ);
try {
synchronized (chunk) {
return chunk.get(0, 0, 0, NativeStructureOwnershipBundle.class);
@@ -279,7 +279,7 @@ public final class NativeStructureOwnershipStore {
RuntimeMantleOwner owner = owner();
int targetChunkX = unpackX(target);
int targetChunkZ = unpackZ(target);
MantleChunk<Matter> chunk = owner.mantle().getMantle().getChunk(targetChunkX, targetChunkZ).use();
MantleChunk<Matter> chunk = owner.mantle().getMantle().useChunk(targetChunkX, targetChunkZ);
try {
synchronized (chunk) {
Matter section = chunk.getOrCreate(0);
@@ -302,7 +302,7 @@ public final class NativeStructureOwnershipStore {
RuntimeMantleOwner owner = owner();
int targetChunkX = unpackX(target);
int targetChunkZ = unpackZ(target);
MantleChunk<Matter> chunk = owner.mantle().getMantle().getChunk(targetChunkX, targetChunkZ).use();
MantleChunk<Matter> chunk = owner.mantle().getMantle().useChunk(targetChunkX, targetChunkZ);
try {
synchronized (chunk) {
Matter section = chunk.getOrCreate(0);
@@ -396,7 +396,7 @@ public final class NativeStructureOwnershipStore {
private void restorePendingBundle(Mantle<Matter> mantle, long target,
NativeStructureOwnershipBundle bundle) {
MantleChunk<Matter> chunk = mantle.getChunk(unpackX(target), unpackZ(target)).use();
MantleChunk<Matter> chunk = mantle.useChunk(unpackX(target), unpackZ(target));
try {
synchronized (chunk) {
Matter section = chunk.getOrCreate(0);
@@ -154,8 +154,7 @@ public final class IrisStaticObjectLayer {
return;
}
persistContinuations(engine, x >> 4, z >> 4, output.getHeight());
MantleChunk<Matter> chunk = engine.getMantle().getMantle().getChunk(x >> 4, z >> 4);
chunk.use();
MantleChunk<Matter> chunk = engine.getMantle().getMantle().useChunk(x >> 4, z >> 4);
try {
for (Block block : blocks) {
Matter section = chunk.getOrCreate(block.y() >> 4);
@@ -574,7 +574,7 @@ public final class GenerationHistoryRuntimeRouter implements AutoCloseable {
SavedMantleAccess access = acquireSavedMantle(activation, requireEpoch(activation));
try {
Mantle<Matter> mantle = access.mantle();
MantleChunk<Matter> anchor = mantle.getChunk(regionX << 5, regionZ << 5).use();
MantleChunk<Matter> anchor = mantle.useChunk(regionX << 5, regionZ << 5);
try {
TectonicPlate<Matter> plate = mantle.getLoadedRegion(regionX, regionZ);
HashMap<Long, ObjectContinuationBundle> bundles = new HashMap<>();
@@ -169,8 +169,7 @@ public class EngineMantleCleanupTest {
MantleChunk<Matter> chunk = mock(MantleChunk.class);
when(engineMantle.getMantle()).thenReturn(mantle);
doReturn(true).when(engineMantle).isCovered(7, -4);
when(mantle.getChunk(7, -4)).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(7, -4)).thenReturn(chunk);
doAnswer(invocation -> {
Runnable cleanup = invocation.getArgument(1);
cleanup.run();
@@ -197,7 +197,7 @@ public class EngineMantleCoverageTest {
assertTrue(actual.isEmpty());
verify(fixture.mantle(), never()).hasLoadedFlag(anyInt(), anyInt(), any());
verify(fixture.mantle(), never()).getChunk(anyInt(), anyInt());
verify(fixture.mantle(), never()).useChunk(anyInt(), anyInt());
when(fixture.mantle().isChunkLoaded(anyInt(), anyInt())).thenReturn(true);
fixture.engine().cleanupChunksCoveredBy(0, 0, true,
@@ -217,7 +217,7 @@ public class EngineMantleCoverageTest {
assertTrue(actual.isEmpty());
assertTrue(fixture.cleaned().isEmpty());
verify(fixture.mantle(), never()).getChunk(anyInt(), anyInt());
verify(fixture.mantle(), never()).useChunk(anyInt(), anyInt());
verify(fixture.mantle(), never()).hasFlag(anyInt(), anyInt(), any());
}
@@ -233,7 +233,7 @@ public class EngineMantleCoverageTest {
assertTrue(actual.isEmpty());
verify(fixture.mantle(), never()).withLoadedChunk(anyInt(), anyInt(), any());
verify(fixture.mantle(), never()).getChunk(anyInt(), anyInt());
verify(fixture.mantle(), never()).useChunk(anyInt(), anyInt());
verify(fixture.mantle(), never()).hasFlag(anyInt(), anyInt(), any());
}
@@ -270,7 +270,7 @@ public class EngineMantleCoverageTest {
});
}
assertEquals(List.of(new Chunk(0, 0)), actual);
verify(fixture.mantle(), never()).getChunk(anyInt(), anyInt());
verify(fixture.mantle(), never()).useChunk(anyInt(), anyInt());
verify(fixture.mantle(), never()).hasFlag(anyInt(), anyInt(), any());
}
@@ -112,10 +112,8 @@ public class FloatingObjectPlacementTransactionTest {
when(engine.getHeight()).thenReturn(128);
when(engine.getMantle()).thenReturn(engineMantle);
when(engineMantle.getMantle()).thenReturn(mantle);
when(mantle.getChunk(0, 0)).thenReturn(left);
when(mantle.getChunk(1, 0)).thenReturn(right);
when(left.use()).thenReturn(left);
when(right.use()).thenReturn(right);
when(mantle.useChunk(0, 0)).thenReturn(left);
when(mantle.useChunk(1, 0)).thenReturn(right);
when(left.getOrCreate(0)).thenReturn(leftSection);
when(right.getOrCreate(0)).thenReturn(rightSection);
NativeBlockState state = mock(NativeBlockState.class);
@@ -146,8 +144,7 @@ public class FloatingObjectPlacementTransactionTest {
MantleChunk<Matter> chunk = mock(MantleChunk.class);
when(engine.getMantle()).thenReturn(engineMantle);
when(engineMantle.getMantle()).thenReturn(mantle);
when(mantle.getChunk(anyInt(), anyInt())).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(anyInt(), anyInt())).thenReturn(chunk);
when(chunk.isFlagged(MantleFlag.REAL)).thenReturn(true);
when(placer.getEngine()).thenReturn(engine);
when(placer.canWriteObjectBlock(anyInt(), anyInt(), anyInt())).thenAnswer(invocation -> {
@@ -67,6 +67,7 @@ public class MantleCarvingComponentBoundaryRadiusTest {
MantleChunk<Matter> chunk = mock(MantleChunk.class);
when(mantle.getChunk(anyInt(), anyInt())).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(anyInt(), anyInt())).thenAnswer(call -> chunk.use());
doAnswer(invocation -> {
Runnable task = invocation.getArgument(1);
task.run();
@@ -319,6 +319,7 @@ public class MantleObjectComponentBoundaryRadiusTest {
when(mantle.getWorldHeight()).thenReturn(64);
when(mantle.getChunk(anyInt(), anyInt())).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(anyInt(), anyInt())).thenAnswer(call -> chunk.use());
doAnswer(invocation -> {
Runnable task = invocation.getArgument(1);
task.run();
@@ -68,6 +68,7 @@ public class MantleWriterOverlayTest {
when(engine.getDimension()).thenReturn(dimension);
when(mantle.getChunk(0, 0)).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(0, 0)).thenAnswer(call -> chunk.use());
when(chunk.getOrCreate(0)).thenReturn(matter);
when(chunk.exists(0)).thenReturn(true);
when(chunk.get(0)).thenReturn(matter);
@@ -82,6 +82,7 @@ public class MantleWriterPreObjectJournalTest {
when(mantle.getWorldHeight()).thenReturn(32);
when(mantle.getChunk(0, 0)).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(0, 0)).thenAnswer(call -> chunk.use());
when(chunk.exists(0)).thenReturn(true);
when(chunk.getOrCreate(0)).thenReturn(matter);
when(chunk.get(0)).thenReturn(matter);
@@ -39,6 +39,7 @@ public class MatterGeneratorCarvePassRadiusTest {
MantleChunk<Matter> chunk = mock(MantleChunk.class);
when(mantle.getChunk(anyInt(), anyInt())).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(anyInt(), anyInt())).thenAnswer(call -> chunk.use());
doAnswer(invocation -> {
Runnable task = invocation.getArgument(1);
task.run();
@@ -76,6 +77,7 @@ public class MatterGeneratorCarvePassRadiusTest {
MantleChunk<Matter> chunk = mock(MantleChunk.class);
when(mantle.getChunk(anyInt(), anyInt())).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(anyInt(), anyInt())).thenAnswer(call -> chunk.use());
doAnswer(invocation -> {
Runnable task = invocation.getArgument(1);
task.run();
@@ -107,6 +109,7 @@ public class MatterGeneratorCarvePassRadiusTest {
MantleChunk<Matter> chunk = mock(MantleChunk.class);
when(mantle.getChunk(anyInt(), anyInt())).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(anyInt(), anyInt())).thenAnswer(call -> chunk.use());
doAnswer(invocation -> {
Runnable task = invocation.getArgument(1);
task.run();
@@ -144,6 +147,7 @@ public class MatterGeneratorCarvePassRadiusTest {
MantleChunk<Matter> chunk = mock(MantleChunk.class);
when(mantle.getChunk(anyInt(), anyInt())).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(anyInt(), anyInt())).thenAnswer(call -> chunk.use());
doAnswer(invocation -> {
Runnable task = invocation.getArgument(1);
task.run();
@@ -184,6 +188,7 @@ public class MatterGeneratorCarvePassRadiusTest {
MantleChunk<Matter> chunk = mock(MantleChunk.class);
when(mantle.getChunk(anyInt(), anyInt())).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(anyInt(), anyInt())).thenAnswer(call -> chunk.use());
doAnswer(invocation -> {
Runnable task = invocation.getArgument(1);
task.run();
@@ -776,6 +776,8 @@ public class MatterGeneratorConcurrencyTest {
long key = (((long) chunkX) << 32) ^ (chunkZ & 0xffffffffL);
return chunks.computeIfAbsent(key, ignored -> lockingChunks ? lockingChunk() : chunk());
});
when(mantle.useChunk(anyInt(), anyInt())).thenAnswer(invocation ->
mantle.getChunk(invocation.getArgument(0), invocation.getArgument(1)).use());
context = mock(ChunkContext.class);
when(context.getGenerationSessionId()).thenReturn(91L);
}
@@ -246,10 +246,8 @@ public class ObjectContinuationTest {
Mantle<Matter> mantle = mock(Mantle.class);
MantleChunk<Matter> saved = mock(MantleChunk.class);
MantleChunk<Matter> future = mock(MantleChunk.class);
when(mantle.getChunk(0, 0)).thenReturn(saved);
when(mantle.getChunk(1, 0)).thenReturn(future);
when(saved.use()).thenReturn(saved);
when(future.use()).thenReturn(future);
when(mantle.useChunk(0, 0)).thenReturn(saved);
when(mantle.useChunk(1, 0)).thenReturn(future);
when(saved.isFlagged(MantleFlag.REAL)).thenReturn(true);
Matter section = new IrisMatter(16, 16, 16);
when(future.getOrCreate(0)).thenReturn(section);
@@ -634,6 +634,7 @@ public class ObjectDestinationTransactionTest {
when(mantle.getWorldHeight()).thenReturn(16);
when(mantle.getChunk(0, 0)).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(0, 0)).thenAnswer(call -> chunk.use());
when(chunk.exists(0)).thenReturn(true);
when(chunk.getOrCreate(0)).thenReturn(matter);
when(chunk.get(0)).thenReturn(matter);
@@ -80,6 +80,7 @@ public class TerrainPlacementQueryTest {
MantleChunk<Matter> chunk = mock(MantleChunk.class);
when(mantle.getChunk(0, 0)).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(0, 0)).thenAnswer(call -> chunk.use());
when(chunk.exists(0)).thenReturn(true);
matter = new IrisMatter(16, 16, 16);
when(chunk.get(0)).thenReturn(matter);
@@ -308,6 +308,7 @@ public class DimensionTerrain3DCompositionTest {
when(engineMantle.getMantle()).thenReturn(mantle);
when(mantle.getChunk(0, 0)).thenReturn(mantleChunk);
when(mantleChunk.use()).thenReturn(mantleChunk);
when(mantle.useChunk(0, 0)).thenAnswer(call -> mantleChunk.use());
when(context.getDimensionStackLayout(0, 0)).thenReturn(layout);
Hunk<NativeBlockState> output = Hunk.newArrayHunk(1, height, 1);
for (int y = 0; y < height; y++) {
@@ -106,11 +106,10 @@ public class EngineBiomeColumnTest {
when(mantle.hasTectonicPlate(anyInt(), anyInt())).thenReturn(true);
when(mantle.get(anyInt(), anyInt(), anyInt(), eq(MatterCavern.class))).thenAnswer(call ->
cavern(salt, call.getArgument(0), call.getArgument(1), call.getArgument(2)));
when(mantle.getChunk(anyInt(), anyInt())).thenAnswer(call -> {
when(mantle.useChunk(anyInt(), anyInt())).thenAnswer(call -> {
int chunkX = call.getArgument(0);
int chunkZ = call.getArgument(1);
MantleChunk<Matter> chunk = mock(MantleChunk.class);
when(chunk.use()).thenReturn(chunk);
when(chunk.get(anyInt(), anyInt(), anyInt(), eq(MatterCavern.class))).thenAnswer(read -> cavern(salt,
chunkX * 16 + (int) read.getArgument(0), read.getArgument(1), chunkZ * 16 + (int) read.getArgument(2)));
return chunk;
@@ -184,8 +184,7 @@ public class IrisCarveWallPaletteScopeTest {
chunk = mock(MantleChunk.class);
EngineMantle engineMantle = mock(EngineMantle.class);
doReturn(mantle).when(engineMantle).getMantle();
doReturn(chunk).when(mantle).getChunk(-2, 3);
doReturn(chunk).when(chunk).use();
doReturn(chunk).when(mantle).useChunk(-2, 3);
IrisMatterSupport.ensureRegistered();
Matter section = new IrisMatter(16, 16, 16);
for (int coordinate : new int[]{4, 8, 12}) {
@@ -58,7 +58,7 @@ public class WorldChunkMaintenanceRoutingTest {
public void materializesTheSavedOwnerAndKeepsSpawningAfterCompletion() throws Exception {
try (Fixture fixture = new Fixture()) {
fixture.schedule();
verify(fixture.currentMantle, never()).getChunk(2, -1);
verify(fixture.currentMantle, never()).useChunk(2, -1);
fixture.prepare();
verify(fixture.savedChunk).use();
@@ -285,6 +285,7 @@ public class WorldChunkMaintenanceRoutingTest {
when(saved.mantle()).thenReturn(savedMantle);
when(savedMantle.getChunk(2, -1)).thenReturn(savedChunk);
when(savedChunk.use()).thenReturn(savedChunk);
when(savedMantle.useChunk(2, -1)).thenAnswer(invocation -> savedMantle.getChunk(2, -1).use());
when(savedMantle.isChunkLoaded(2, -1)).thenReturn(true);
when(savedMantle.hasFlag(2, -1, MantleFlag.ETCHED)).thenAnswer(invocation -> etched.get());
when(router.openSavedChunkMantle(2, -1)).thenReturn(saved);
@@ -177,8 +177,7 @@ public class IrisDepositModifierContextTest {
.thenReturn(new KList<>(biomeDeposit));
chunk = mock(MantleChunk.class);
Mantle<Matter> mantle = engine.getMantle().getMantle();
doReturn(chunk).when(mantle).getChunk(2, -3);
when(chunk.use()).thenReturn(chunk);
doReturn(chunk).when(mantle).useChunk(2, -3);
if (engine instanceof IrisEngine irisEngine) {
when(irisEngine.hasGenerationRuntimeScope()).thenReturn(scoped);
when(irisEngine.captureGenerationRuntimeBinding()).thenReturn(binding);
@@ -296,8 +296,7 @@ public class IrisDepositModifierOrderingTest {
when(context.getGenerationSessionId()).thenReturn(11L);
chunk = mock(MantleChunk.class);
Mantle<Matter> mantle = engine.getMantle().getMantle();
doReturn(chunk).when(mantle).getChunk(0, 0);
when(chunk.use()).thenReturn(chunk);
doReturn(chunk).when(mantle).useChunk(0, 0);
MultiBurst pool = mock(MultiBurst.class);
BurstExecutor burst = new BurstExecutor(executor, 3);
burst.setMulticore(parallel);
@@ -193,8 +193,7 @@ public class IrisDepositModifierParityTest {
when(context.getRegion().get(anyInt(), anyInt())).thenReturn(region);
MantleChunk<Matter> chunk = mock(MantleChunk.class);
Mantle<Matter> mantle = engine.getMantle().getMantle();
doReturn(chunk).when(mantle).getChunk(-3, 5);
when(chunk.use()).thenReturn(chunk);
doReturn(chunk).when(mantle).useChunk(-3, 5);
MatterCavern cavern = new MatterCavern(true, "allowed", (byte) 0);
when(chunk.get(anyInt(), anyInt(), anyInt(), any())).thenAnswer(invocation -> {
int x = invocation.getArgument(0);
@@ -81,18 +81,18 @@ public class IrisNaturalCaveScanTest {
Fixture fixture = new Fixture();
ChunkContext context = mock(ChunkContext.class);
doReturn(CaveTerrainSnapshot.capture(fixture.chunk, -2, 3)).when(context).getCaveTerrain();
clearInvocations(fixture.chunk);
clearInvocations(fixture.mantle);
fixture.modifier.decorateNaturalCaves(-32, 48, fixture.output(3, 2), context);
verify(fixture.chunk, never()).use();
verify(fixture.mantle, never()).useChunk(anyInt(), anyInt());
verify(context).setCaveTerrain(null);
assertFalse(fixture.calls.isEmpty());
Fixture other = new Fixture();
ChunkContext stale = mock(ChunkContext.class);
doReturn(CaveTerrainSnapshot.capture(other.chunk, 5, 5)).when(stale).getCaveTerrain();
clearInvocations(other.chunk);
clearInvocations(other.mantle);
other.modifier.decorateNaturalCaves(-32, 48, other.output(3, 2), stale);
verify(other.chunk).use();
verify(other.mantle).useChunk(-2, 3);
assertEquals(fixture.calls, other.calls);
}
@@ -213,7 +213,7 @@ public class IrisNaturalCaveScanTest {
assertEquals(expectedWrites, writes);
assertEquals(expectedMarkers, fixture.markers);
assertTrue("fixture must exercise marker writes", !expectedMarkers.isEmpty());
verify(fixture.chunk).use();
verify(fixture.mantle).useChunk(-2, 3);
verify(fixture.chunk).release();
}
@@ -236,6 +236,7 @@ public class IrisNaturalCaveScanTest {
private final NativeBlockState floor = block("minecraft:moss_block", true);
private final NativeBlockState ceiling = block("minecraft:calcite", true);
private final IrisCarveModifier modifier = mock(IrisCarveModifier.class, CALLS_REAL_METHODS);
private final Mantle<Matter> mantle;
private final MantleChunk<Matter> chunk;
private final List<String> calls = new ArrayList<>();
private final List<String> markers = new ArrayList<>();
@@ -248,7 +249,7 @@ public class IrisNaturalCaveScanTest {
private Fixture(boolean buildAboveTop) throws Exception {
Engine engine = mock(Engine.class);
EngineMantle engineMantle = mock(EngineMantle.class);
Mantle<Matter> mantle = mock(Mantle.class);
mantle = mock(Mantle.class);
chunk = mock(MantleChunk.class);
doReturn(engine).when(modifier).getEngine();
doReturn(mock(IrisComplex.class)).when(modifier).getComplex();
@@ -256,8 +257,7 @@ public class IrisNaturalCaveScanTest {
doReturn(new SeedManager(1337L)).when(engine).getSeedManager();
doReturn(engineMantle).when(engine).getMantle();
doReturn(mantle).when(engineMantle).getMantle();
doReturn(chunk).when(mantle).getChunk(-2, 3);
doReturn(chunk).when(chunk).use();
doReturn(chunk).when(mantle).useChunk(-2, 3);
doAnswer(call -> {
markers.add(call.getArgument(0) + ":" + call.getArgument(1) + ":" + call.getArgument(2) + ":" + call.getArgument(3));
return null;
@@ -87,7 +87,7 @@ public class EngineBukkitOpsDeferredMaterializationTest {
Block block = mock(Block.class);
when(chunk.getBlock(1, 2, 3)).thenReturn(block);
when(saved.isLoaded(chunk)).thenReturn(true);
when(saved.getChunk(chunk)).thenReturn(stored);
when(saved.useChunk(chunk)).thenAnswer(invocation -> stored.use());
ExternalDataSVC provider = mock(ExternalDataSVC.class);
try (MockedStatic<J> scheduling = mockStatic(J.class);
@@ -456,9 +456,9 @@ public class NativeStructureOwnershipStoreTest {
currentRuntime.set(12);
assertTrue(storage.flush());
verify(mantleA.mantle(), times(2)).getChunk(4, -5);
verify(mantleA.mantle(), times(2)).useChunk(4, -5);
verify(mantleA.mantle()).saveIdleTectonicPlates(any());
verify(mantleB.mantle(), never()).getChunk(anyInt(), anyInt());
verify(mantleB.mantle(), never()).useChunk(anyInt(), anyInt());
verify(mantleB.mantle(), never()).saveIdleTectonicPlates(any());
storage.close();
}
@@ -491,9 +491,9 @@ public class NativeStructureOwnershipStoreTest {
storage.write(NativeStructureOwnershipStore.pack(9, -10), recordB);
assertTrue(storage.flush());
verify(mantleA.mantle(), times(2)).getChunk(-7, 8);
verify(mantleA.mantle(), times(2)).useChunk(-7, 8);
verify(mantleA.mantle()).saveIdleTectonicPlates(any());
verify(mantleB.mantle(), times(2)).getChunk(9, -10);
verify(mantleB.mantle(), times(2)).useChunk(9, -10);
verify(mantleB.mantle()).saveIdleTectonicPlates(any());
storage.close();
}
@@ -519,8 +519,7 @@ public class NativeStructureOwnershipStoreTest {
Matter section = mock(Matter.class);
MatterSlice<NativeStructureOwnershipBundle> slice = mock(MatterSlice.class);
when(engineMantle.getMantle()).thenReturn(mantle);
when(mantle.getChunk(anyInt(), anyInt())).thenReturn(chunk);
when(chunk.use()).thenReturn(chunk);
when(mantle.useChunk(anyInt(), anyInt())).thenReturn(chunk);
when(chunk.getOrCreate(0)).thenReturn(section);
when(section.<NativeStructureOwnershipBundle>slice(
NativeStructureOwnershipBundle.class)).thenReturn(slice);
@@ -187,9 +187,8 @@ public class IrisStaticObjectLayerTest {
MatterSlice<TileWrapper> tiles = mock(MatterSlice.class);
when(engine.getMantle()).thenReturn(engineMantle);
when(engineMantle.getMantle()).thenReturn(mantle);
when(mantle.getChunk(anyInt(), anyInt())).thenReturn(chunk);
when(mantle.useChunk(anyInt(), anyInt())).thenReturn(chunk);
when(chunk.getOrCreate(anyInt())).thenReturn(section);
when(chunk.use()).thenReturn(chunk);
when(section.getSlice(TileWrapper.class)).thenReturn(tiles);
when(chunk.isFlagged(MantleFlag.REAL)).thenReturn(true);
Hunk<NativeBlockState> left = Hunk.newArrayHunk(16, 384, 16);
@@ -402,7 +401,7 @@ public class IrisStaticObjectLayerTest {
when(engine.getMantle()).thenReturn(engineMantle);
when(engineMantle.getMantle()).thenReturn(mantle);
when(mantle.getWorldHeight()).thenReturn(384);
when(mantle.getChunk(anyInt(), anyInt())).thenAnswer(call -> chunk(call.getArgument(0), call.getArgument(1)));
when(mantle.useChunk(anyInt(), anyInt())).thenAnswer(call -> chunk(call.getArgument(0), call.getArgument(1)));
}
@SuppressWarnings("unchecked")
@@ -410,7 +409,6 @@ public class IrisStaticObjectLayerTest {
return chunks.computeIfAbsent(new ObjectContinuationBundle.ChunkPosition(x, z), key -> {
MantleChunk<Matter> chunk = mock(MantleChunk.class);
Map<Integer, Matter> sections = new HashMap<>();
when(chunk.use()).thenReturn(chunk);
when(chunk.get(anyInt())).thenAnswer(call -> sections.get((int) call.getArgument(0)));
when(chunk.getOrCreate(anyInt())).thenAnswer(call -> sections.computeIfAbsent(call.getArgument(0),
section -> new IrisMatter(16, 16, 16)));
@@ -290,7 +290,7 @@ public final class ObjectContinuationProbeTest {
}
GenerationHistoryRuntimeRouter router = engine.getGenerationHistoryRuntimeRouter().orElseThrow();
try (GenerationHistoryRuntimeRouter.SavedChunkMantle saved = router.openSavedChunkMantle(0, 0)) {
MantleChunk<Matter> chunk = saved.mantle().getChunk(0, 0).use();
MantleChunk<Matter> chunk = saved.mantle().useChunk(0, 0);
try {
for (Map.Entry<Position, String> edit : edits.entrySet()) {
Position position = edit.getKey();