mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-18 22:44:09 +00:00
Cleared records under native writes
- Added DeferredPlacementRecords to gate tracking on Identifier and TileWrapper slices - Cleared deferred custom and tile records at positions native structure placement changed - Implemented block change tracking in the Bukkit and modded structure policies - Covered invalidation through the custom block materialization pass - Covered modded wrapper change tracking and protected cells - Passed disabled change sets in existing wrapper tests
This commit is contained in:
+7
-3
@@ -3,6 +3,7 @@ package art.arcane.iris.platform.bukkit.nms.v26_2_R1;
|
||||
import art.arcane.volmlib.nativelib.v26_2_R1.terrain.NativeStructureWorldgenAccess;
|
||||
|
||||
import art.arcane.iris.structure.placement.IrisStructureTerrain;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockChangeSet;
|
||||
import art.arcane.volmlib.util.structure.StructureTerrainMode;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructurePostProcessor;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureTerrainIntegrator;
|
||||
@@ -231,7 +232,8 @@ public class NativeStructureWorldgenAccessTest {
|
||||
(x, z) -> SURFACE_FIRST_FREE_Y,
|
||||
(x, z) -> FLOOR_FIRST_FREE_Y,
|
||||
true,
|
||||
position -> false);
|
||||
position -> false,
|
||||
NativeBlockChangeSet.disabled());
|
||||
int x = generationCenter.getMiddleBlockX();
|
||||
int z = generationCenter.getMiddleBlockZ();
|
||||
|
||||
@@ -249,7 +251,8 @@ public class NativeStructureWorldgenAccessTest {
|
||||
(x, z) -> SURFACE_FIRST_FREE_Y,
|
||||
(x, z) -> FLOOR_FIRST_FREE_Y,
|
||||
false,
|
||||
position::equals);
|
||||
position::equals,
|
||||
NativeBlockChangeSet.disabled());
|
||||
|
||||
assertSame(Blocks.DIRT, access.getBlockState(position).getBlock());
|
||||
assertFalse(access.ensureCanWrite(position));
|
||||
@@ -320,7 +323,8 @@ public class NativeStructureWorldgenAccessTest {
|
||||
(x, z) -> SURFACE_FIRST_FREE_Y,
|
||||
(x, z) -> FLOOR_FIRST_FREE_Y,
|
||||
false,
|
||||
position -> false);
|
||||
position -> false,
|
||||
NativeBlockChangeSet.disabled());
|
||||
}
|
||||
|
||||
private static ChunkPos generationCenter() {
|
||||
|
||||
+7
-3
@@ -3,6 +3,7 @@ package art.arcane.iris.platform.bukkit.nms.v26_3_R1;
|
||||
import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureWorldgenAccess;
|
||||
|
||||
import art.arcane.iris.structure.placement.IrisStructureTerrain;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockChangeSet;
|
||||
import art.arcane.volmlib.util.structure.StructureTerrainMode;
|
||||
import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructurePostProcessor;
|
||||
import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureTerrainIntegrator;
|
||||
@@ -231,7 +232,8 @@ public class NativeStructureWorldgenAccessTest {
|
||||
(x, z) -> SURFACE_FIRST_FREE_Y,
|
||||
(x, z) -> FLOOR_FIRST_FREE_Y,
|
||||
true,
|
||||
position -> false);
|
||||
position -> false,
|
||||
NativeBlockChangeSet.disabled());
|
||||
int x = generationCenter.getMiddleBlockX();
|
||||
int z = generationCenter.getMiddleBlockZ();
|
||||
|
||||
@@ -249,7 +251,8 @@ public class NativeStructureWorldgenAccessTest {
|
||||
(x, z) -> SURFACE_FIRST_FREE_Y,
|
||||
(x, z) -> FLOOR_FIRST_FREE_Y,
|
||||
false,
|
||||
position::equals);
|
||||
position::equals,
|
||||
NativeBlockChangeSet.disabled());
|
||||
|
||||
assertSame(Blocks.DIRT, access.getBlockState(position).getBlock());
|
||||
assertFalse(access.ensureCanWrite(position));
|
||||
@@ -320,7 +323,8 @@ public class NativeStructureWorldgenAccessTest {
|
||||
(x, z) -> SURFACE_FIRST_FREE_Y,
|
||||
(x, z) -> FLOOR_FIRST_FREE_Y,
|
||||
false,
|
||||
position -> false);
|
||||
position -> false,
|
||||
NativeBlockChangeSet.disabled());
|
||||
}
|
||||
|
||||
private static ChunkPos generationCenter() {
|
||||
|
||||
+12
@@ -2,6 +2,7 @@ package art.arcane.iris.modded;
|
||||
|
||||
import art.arcane.volmlib.nativelib.terrain.structure.StructureStagePolicy;
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.generation.mantle.DeferredPlacementRecords;
|
||||
import art.arcane.iris.generation.terrain.IrisMaterialPalette;
|
||||
import art.arcane.iris.structure.nativegen.GenerationWritePolicy;
|
||||
import art.arcane.iris.structure.nativegen.IrisStructurePolicy;
|
||||
@@ -13,6 +14,7 @@ import art.arcane.iris.structure.nativegen.StructureLocateSearch;
|
||||
import art.arcane.iris.structure.placement.IrisStructureLocator;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedStructureStage;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVolumeSource;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockChangeSet;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockPositionPredicate;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
|
||||
import art.arcane.volmlib.nativelib.terrain.structure.StructureLocateSearchAccess;
|
||||
@@ -107,6 +109,16 @@ final class ModdedNativeStructurePolicy implements NativeModdedStructureStage.Po
|
||||
context.getDimensionStackContext(), context.getMinHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeBlockChangeSet trackBlockChanges(Engine context, NativeBlockChangeSet.Bounds bounds) {
|
||||
return DeferredPlacementRecords.track(context, bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateBlockChanges(Engine context, NativeBlockChangeSet changes) {
|
||||
DeferredPlacementRecords.invalidate(context, changes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntBinaryOperator surfaceHeight(Engine context) {
|
||||
return (x, z) -> Engine.hostHeight(context, x, z, true) + context.getMinHeight();
|
||||
|
||||
+38
-3
@@ -3,6 +3,7 @@ package art.arcane.iris.modded;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedNativeStructureWorldgenAccess;
|
||||
|
||||
import art.arcane.iris.structure.placement.IrisStructureTerrain;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockChangeSet;
|
||||
import art.arcane.volmlib.util.structure.StructureTerrainMode;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructurePostProcessor;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureTerrainIntegrator;
|
||||
@@ -231,7 +232,8 @@ public class ModdedNativeStructureWorldgenAccessTest {
|
||||
(x, z) -> SURFACE_FIRST_FREE_Y,
|
||||
(x, z) -> FLOOR_FIRST_FREE_Y,
|
||||
true,
|
||||
position -> false);
|
||||
position -> false,
|
||||
NativeBlockChangeSet.disabled());
|
||||
int x = generationCenter.getMiddleBlockX();
|
||||
int z = generationCenter.getMiddleBlockZ();
|
||||
|
||||
@@ -249,7 +251,8 @@ public class ModdedNativeStructureWorldgenAccessTest {
|
||||
(x, z) -> SURFACE_FIRST_FREE_Y,
|
||||
(x, z) -> FLOOR_FIRST_FREE_Y,
|
||||
false,
|
||||
position::equals);
|
||||
position::equals,
|
||||
NativeBlockChangeSet.disabled());
|
||||
|
||||
assertSame(Blocks.DIRT, access.getBlockState(position).getBlock());
|
||||
assertFalse(access.ensureCanWrite(position));
|
||||
@@ -270,6 +273,37 @@ public class ModdedNativeStructureWorldgenAccessTest {
|
||||
assertEquals(1, recording.mutations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void acceptedWritesAndReportedChangesAreTrackedOutsideProtectedCells() {
|
||||
RecordingDelegate recording = new RecordingDelegate();
|
||||
BlockPos protectedCell = generationCenter().getMiddleBlockPosition(70);
|
||||
WorldGenLevel delegate = recording.world();
|
||||
NativeBlockChangeSet changes = NativeBlockChangeSet.create(
|
||||
ModdedNativeStructureWorldgenAccess.changeBounds(delegate, generationCenter()));
|
||||
ModdedNativeStructureWorldgenAccess access = ModdedNativeStructureWorldgenAccess.create(
|
||||
delegate, generationCenter(),
|
||||
(x, z) -> SURFACE_FIRST_FREE_Y,
|
||||
(x, z) -> FLOOR_FIRST_FREE_Y,
|
||||
false,
|
||||
protectedCell::equals,
|
||||
changes);
|
||||
BlockPos written = protectedCell.above();
|
||||
BlockPos removed = protectedCell.east();
|
||||
BlockPos reported = protectedCell.above(2);
|
||||
|
||||
assertFalse(access.setBlock(protectedCell, Blocks.STONE.defaultBlockState(), 2));
|
||||
assertTrue(access.setBlock(written, Blocks.STONE.defaultBlockState(), 2));
|
||||
assertTrue(access.removeBlock(removed, false));
|
||||
access.recordBlockChange(reported.getX(), reported.getY(), reported.getZ());
|
||||
access.recordBlockChange(protectedCell.getX(), protectedCell.getY(), protectedCell.getZ());
|
||||
|
||||
assertEquals(3, changes.size());
|
||||
assertTrue(changes.contains(written.getX(), written.getY(), written.getZ()));
|
||||
assertTrue(changes.contains(removed.getX(), removed.getY(), removed.getZ()));
|
||||
assertTrue(changes.contains(reported.getX(), reported.getY(), reported.getZ()));
|
||||
assertFalse(changes.contains(protectedCell.getX(), protectedCell.getY(), protectedCell.getZ()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void statuslessChunkReadsPreserveWorldgenDelegateSemantics() {
|
||||
RecordingDelegate recording = new RecordingDelegate();
|
||||
@@ -320,7 +354,8 @@ public class ModdedNativeStructureWorldgenAccessTest {
|
||||
(x, z) -> SURFACE_FIRST_FREE_Y,
|
||||
(x, z) -> FLOOR_FIRST_FREE_Y,
|
||||
false,
|
||||
position -> false);
|
||||
position -> false,
|
||||
NativeBlockChangeSet.disabled());
|
||||
}
|
||||
|
||||
private static ChunkPos generationCenter() {
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package art.arcane.iris.generation.mantle;
|
||||
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.integration.Identifier;
|
||||
import art.arcane.iris.world.storage.matter.TileWrapper;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockChangeSet;
|
||||
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
|
||||
import art.arcane.volmlib.util.matter.Matter;
|
||||
import art.arcane.volmlib.util.matter.MatterSlice;
|
||||
|
||||
public final class DeferredPlacementRecords {
|
||||
private DeferredPlacementRecords() {
|
||||
}
|
||||
|
||||
public static NativeBlockChangeSet track(Engine engine, NativeBlockChangeSet.Bounds bounds) {
|
||||
Mantle<Matter> mantle = engine.getMantle().getMantle();
|
||||
for (int chunkX = bounds.minChunkX(); chunkX <= bounds.maxChunkX(); chunkX++) {
|
||||
for (int chunkZ = bounds.minChunkZ(); chunkZ <= bounds.maxChunkZ(); chunkZ++) {
|
||||
if (hasRecords(mantle, chunkX, chunkZ)) {
|
||||
return NativeBlockChangeSet.create(bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NativeBlockChangeSet.disabled();
|
||||
}
|
||||
|
||||
public static void invalidate(Engine engine, NativeBlockChangeSet changes) {
|
||||
if (changes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Mantle<Matter> mantle = engine.getMantle().getMantle();
|
||||
int minHeight = engine.getMinHeight();
|
||||
changes.forEachChunk((chunkX, chunkZ) -> invalidateChunk(mantle, changes, chunkX, chunkZ, minHeight));
|
||||
}
|
||||
|
||||
private static boolean hasRecords(Mantle<Matter> mantle, int chunkX, int chunkZ) {
|
||||
MantleChunk<Matter> chunk = mantle.useChunk(chunkX, chunkZ);
|
||||
try {
|
||||
synchronized (chunk) {
|
||||
return hasRecords(chunk);
|
||||
}
|
||||
} finally {
|
||||
chunk.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasRecords(MantleChunk<Matter> chunk) {
|
||||
for (int section = 0; section < chunk.sectionCount(); section++) {
|
||||
Matter matter = chunk.get(section);
|
||||
if (matter != null && (matter.hasSlice(Identifier.class) || matter.hasSlice(TileWrapper.class))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void invalidateChunk(Mantle<Matter> mantle, NativeBlockChangeSet changes,
|
||||
int chunkX, int chunkZ, int minHeight) {
|
||||
MantleChunk<Matter> chunk = mantle.useChunk(chunkX, chunkZ);
|
||||
try {
|
||||
synchronized (chunk) {
|
||||
if (!hasRecords(chunk)) {
|
||||
return;
|
||||
}
|
||||
changes.forEachPosition(chunkX, chunkZ, (x, y, z) -> clearRecords(chunk, x & 15, y - minHeight, z & 15));
|
||||
}
|
||||
} finally {
|
||||
chunk.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static void clearRecords(MantleChunk<Matter> chunk, int x, int y, int z) {
|
||||
if (y < 0 || (y >> 4) >= chunk.sectionCount()) {
|
||||
return;
|
||||
}
|
||||
Matter section = chunk.get(y >> 4);
|
||||
if (section == null) {
|
||||
return;
|
||||
}
|
||||
clearRecord(section.getSlice(Identifier.class), x, y & 15, z);
|
||||
clearRecord(section.getSlice(TileWrapper.class), x, y & 15, z);
|
||||
}
|
||||
|
||||
private static void clearRecord(MatterSlice<?> slice, int x, int y, int z) {
|
||||
if (slice != null) {
|
||||
slice.set(x, y, z, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -4,6 +4,7 @@ import art.arcane.iris.generation.terrain.IrisDimension;
|
||||
import art.arcane.volmlib.nativelib.terrain.structure.StructureCachePolicy;
|
||||
import art.arcane.volmlib.nativelib.terrain.structure.StructureStagePolicy;
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.generation.mantle.DeferredPlacementRecords;
|
||||
import art.arcane.iris.generation.terrain.IrisMaterialPalette;
|
||||
import art.arcane.iris.structure.nativegen.GenerationWritePolicy;
|
||||
import art.arcane.iris.structure.nativegen.IrisStructurePolicy;
|
||||
@@ -13,6 +14,7 @@ import art.arcane.iris.structure.nativegen.NativeStructureStartPlan;
|
||||
import art.arcane.iris.structure.nativegen.NativeStructureVolumeIndex;
|
||||
import art.arcane.iris.structure.nativegen.StructureLocateSearch;
|
||||
import art.arcane.iris.structure.placement.IrisStructureLocator;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockChangeSet;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockPositionPredicate;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
|
||||
import art.arcane.volmlib.nativelib.terrain.structure.StructureLocateSearchAccess;
|
||||
@@ -103,6 +105,16 @@ public final class BukkitStructureStagePolicy implements StructureStagePolicy<
|
||||
context.getDimensionStackContext(), context.getMinHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeBlockChangeSet trackBlockChanges(Engine context, NativeBlockChangeSet.Bounds bounds) {
|
||||
return DeferredPlacementRecords.track(context, bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateBlockChanges(Engine context, NativeBlockChangeSet changes) {
|
||||
DeferredPlacementRecords.invalidate(context, changes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntBinaryOperator surfaceHeight(Engine context) {
|
||||
return (x, z) -> Engine.hostHeight(context, x, z, true) + context.getMinHeight();
|
||||
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
package art.arcane.iris.platform.generation;
|
||||
|
||||
import art.arcane.iris.generation.block.TileData;
|
||||
import art.arcane.iris.generation.mantle.DeferredPlacementRecords;
|
||||
import art.arcane.iris.generation.mantle.EngineMantle;
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.generation.runtime.IrisEngineMantle;
|
||||
import art.arcane.iris.integration.ExternalDataSVC;
|
||||
import art.arcane.iris.integration.Identifier;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.iris.spi.IrisPlatform;
|
||||
import art.arcane.iris.spi.IrisPlatforms;
|
||||
import art.arcane.iris.spi.IrisServices;
|
||||
import art.arcane.iris.spi.PlatformRegistries;
|
||||
import art.arcane.iris.testsupport.PlatformLeakGuard;
|
||||
import art.arcane.iris.world.IrisWorld;
|
||||
import art.arcane.iris.world.storage.matter.IrisMatterSupport;
|
||||
import art.arcane.iris.world.storage.matter.PreObjectMatterTest;
|
||||
import art.arcane.iris.world.storage.matter.TileWrapper;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockChangeSet;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
|
||||
import art.arcane.volmlib.util.mantle.runtime.MantleDataAdapter;
|
||||
import art.arcane.volmlib.util.mantle.runtime.MantleHooks;
|
||||
import art.arcane.volmlib.util.matter.Matter;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class DeferredPlacementInvalidationTest {
|
||||
@ClassRule
|
||||
public static final PlatformLeakGuard PLATFORM_GUARD = PlatformLeakGuard.clean();
|
||||
|
||||
private static final int MIN_HEIGHT = -64;
|
||||
private static final int SECTIONS = 24;
|
||||
|
||||
@Test
|
||||
public void nativeWritesClearDeferredRecordsSoMaterializationSkipsThem() {
|
||||
withMantleData(adapter -> {
|
||||
Map<Long, MantleChunk<Matter>> chunks = new HashMap<>();
|
||||
MantleChunk<Matter> stored = chunk(chunks, adapter, 0, 0);
|
||||
Identifier replaced = Identifier.fromString("craftengine:fence_post");
|
||||
Identifier kept = Identifier.fromString("craftengine:lantern");
|
||||
setRecord(stored, 3, 70, 5, Identifier.class, replaced);
|
||||
setRecord(stored, 3, 76, 5, Identifier.class, kept);
|
||||
setRecord(stored, 4, 70, 5, TileWrapper.class,
|
||||
new TileWrapper(new TileData("minecraft:chest", new KMap<>())));
|
||||
Engine engine = engine(chunks, adapter);
|
||||
|
||||
NativeBlockChangeSet changes = DeferredPlacementRecords.track(engine, bounds());
|
||||
changes.add(3, 70, 5);
|
||||
changes.add(4, 70, 5);
|
||||
DeferredPlacementRecords.invalidate(engine, changes);
|
||||
|
||||
assertNull(record(stored, 4, 70, 5, TileWrapper.class));
|
||||
assertEquals(kept, record(stored, 3, 76, 5, Identifier.class));
|
||||
World world = mock(World.class);
|
||||
when(world.getMinHeight()).thenReturn(MIN_HEIGHT);
|
||||
when(world.getMaxHeight()).thenReturn(320);
|
||||
Chunk bukkitChunk = mock(Chunk.class);
|
||||
when(bukkitChunk.getWorld()).thenReturn(world);
|
||||
Block replacedBlock = mock(Block.class);
|
||||
Block keptBlock = mock(Block.class);
|
||||
when(bukkitChunk.getBlock(3, 70, 5)).thenReturn(replacedBlock);
|
||||
when(bukkitChunk.getBlock(3, 76, 5)).thenReturn(keptBlock);
|
||||
ExternalDataSVC provider = mock(ExternalDataSVC.class);
|
||||
try (MockedStatic<IrisServices> services = mockStatic(IrisServices.class)) {
|
||||
services.when(() -> IrisServices.get(ExternalDataSVC.class)).thenReturn(provider);
|
||||
|
||||
EngineBukkitOps.materializeCustomBlocks(engine, bukkitChunk, stored);
|
||||
}
|
||||
|
||||
verify(provider).processUpdate(engine, keptBlock, kept);
|
||||
verify(provider, never()).processUpdate(eq(engine), eq(replacedBlock), any(Identifier.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void regionsWithoutDeferredRecordsAreNotTracked() {
|
||||
withMantleData(adapter -> {
|
||||
Map<Long, MantleChunk<Matter>> chunks = new HashMap<>();
|
||||
setRecord(chunk(chunks, adapter, 3, 0), 0, 70, 0, Identifier.class,
|
||||
Identifier.fromString("craftengine:outside"));
|
||||
|
||||
assertSame(NativeBlockChangeSet.disabled(),
|
||||
DeferredPlacementRecords.track(engine(chunks, adapter), bounds()));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void neighbouringRecordsEnableTrackingForTheWholeWriteRegion() {
|
||||
withMantleData(adapter -> {
|
||||
Map<Long, MantleChunk<Matter>> chunks = new HashMap<>();
|
||||
setRecord(chunk(chunks, adapter, 1, -1), 0, 70, 0, TileWrapper.class,
|
||||
new TileWrapper(new TileData("minecraft:barrel", new KMap<>())));
|
||||
|
||||
NativeBlockChangeSet changes = DeferredPlacementRecords.track(engine(chunks, adapter), bounds());
|
||||
|
||||
assertNotSame(NativeBlockChangeSet.disabled(), changes);
|
||||
changes.add(16, 70, -16);
|
||||
DeferredPlacementRecords.invalidate(engine(chunks, adapter), changes);
|
||||
assertNull(record(chunks.get(key(1, -1)), 0, 70, 0, TileWrapper.class));
|
||||
});
|
||||
}
|
||||
|
||||
private static NativeBlockChangeSet.Bounds bounds() {
|
||||
return new NativeBlockChangeSet.Bounds(-1, -1, 1, 1, MIN_HEIGHT, 384);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Engine engine(Map<Long, MantleChunk<Matter>> chunks, MantleDataAdapter<Matter> adapter) {
|
||||
Mantle<Matter> mantle = mock(Mantle.class);
|
||||
when(mantle.useChunk(anyInt(), anyInt())).thenAnswer(invocation ->
|
||||
chunk(chunks, adapter, invocation.getArgument(0), invocation.getArgument(1)).use());
|
||||
EngineMantle engineMantle = mock(EngineMantle.class);
|
||||
when(engineMantle.getMantle()).thenReturn(mantle);
|
||||
IrisWorld world = mock(IrisWorld.class);
|
||||
when(world.minHeight()).thenReturn(MIN_HEIGHT);
|
||||
Engine engine = mock(Engine.class);
|
||||
when(engine.getMantle()).thenReturn(engineMantle);
|
||||
when(engine.getWorld()).thenReturn(world);
|
||||
when(engine.getMinHeight()).thenReturn(MIN_HEIGHT);
|
||||
return engine;
|
||||
}
|
||||
|
||||
private static MantleChunk<Matter> chunk(Map<Long, MantleChunk<Matter>> chunks, MantleDataAdapter<Matter> adapter,
|
||||
int chunkX, int chunkZ) {
|
||||
return chunks.computeIfAbsent(key(chunkX, chunkZ),
|
||||
ignored -> new MantleChunk<>(SECTIONS, chunkX, chunkZ, adapter, MantleHooks.NONE));
|
||||
}
|
||||
|
||||
private static long key(int chunkX, int chunkZ) {
|
||||
return ((long) chunkX << 32) | (chunkZ & 0xffffffffL);
|
||||
}
|
||||
|
||||
private static <T> void setRecord(MantleChunk<Matter> chunk, int x, int worldY, int z, Class<T> type, T value) {
|
||||
int y = worldY - MIN_HEIGHT;
|
||||
chunk.getOrCreate(y >> 4).slice(type).set(x & 15, y & 15, z & 15, value);
|
||||
}
|
||||
|
||||
private static <T> T record(MantleChunk<Matter> chunk, int x, int worldY, int z, Class<T> type) {
|
||||
int y = worldY - MIN_HEIGHT;
|
||||
return chunk.get(y >> 4).getSlice(type).get(x & 15, y & 15, z & 15);
|
||||
}
|
||||
|
||||
private static void withMantleData(MantleTest test) {
|
||||
PreObjectMatterTest.setUpBukkit();
|
||||
IrisMatterSupport.ensureRegistered();
|
||||
IrisPlatform previous = IrisPlatforms.isBound() ? IrisPlatforms.get() : null;
|
||||
IrisPlatform platform = mock(IrisPlatform.class);
|
||||
PlatformRegistries registries = mock(PlatformRegistries.class);
|
||||
when(registries.block("AIR")).thenReturn(mock(NativeBlockState.class));
|
||||
when(platform.registries()).thenReturn(registries);
|
||||
IrisPlatforms.unbind();
|
||||
IrisPlatforms.bind(platform);
|
||||
try {
|
||||
test.run(IrisEngineMantle.createRuntimeDataAdapter(mock(IrisData.class)));
|
||||
} finally {
|
||||
IrisPlatforms.unbind();
|
||||
if (previous != null) {
|
||||
IrisPlatforms.bind(previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface MantleTest {
|
||||
void run(MantleDataAdapter<Matter> adapter);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user