Merged stale placement guard

- Combine the carrier check with native write invalidation
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-16 19:37:15 -04:00
commit ed5483d75a
6 files changed
+765 -7

No files matched your search

@@ -31,10 +31,12 @@ import art.arcane.iris.integration.Identifier;
import art.arcane.iris.generation.cache.Cache;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.block.TileData;
import art.arcane.iris.generation.decoration.IrisProceduralBlocks;
import art.arcane.iris.modded.IrisModdedChunkGenerator;
import art.arcane.iris.modded.ModdedBlockResolution;
import art.arcane.iris.modded.ModdedLootApplier;
import art.arcane.iris.modded.ModdedTileData;
import art.arcane.iris.modded.api.ModdedBlockData;
import art.arcane.iris.modded.api.ModdedCustomContentRegistry;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockPlacement;
import art.arcane.iris.spi.IrisLogging;
@@ -50,6 +52,8 @@ import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
@@ -239,16 +243,24 @@ public final class ModdedChunkUpdateService implements ModdedTickableService {
int baseX = chunkX << 4;
int baseZ = chunkZ << 4;
int minHeight = engine.getWorld().minHeight();
StaleCarriers stale = new StaleCarriers();
materializeDeferredSlice(chunk, Identifier.class, () ->
chunk.iterate(Identifier.class, (Integer x, Integer yf, Integer z, Identifier identifier) -> {
int y = yf + minHeight;
if (y < level.minHeight() || y >= level.maxHeight() - 1) {
return;
}
NativeBlockPoint position = new NativeBlockPoint(baseX + (x & 15), y, baseZ + (z & 15));
ModdedCustomContentRegistry.processBlockPlacement(engine, identifier.toString(),
int blockX = baseX + (x & 15);
int blockZ = baseZ + (z & 15);
String key = identifier.toString();
if (stale.carrierReplaced(key, level.getBlock(blockX, y, blockZ))) {
return;
}
NativeBlockPoint position = new NativeBlockPoint(blockX, y, blockZ);
ModdedCustomContentRegistry.processBlockPlacement(engine, key,
() -> NativeWorldMaintenance.placement(level, position));
}));
stale.report(level.name(), chunkX, chunkZ);
}
static void materializeDeferredSlice(MantleChunk<Matter> chunk, Class<?> sliceType, Runnable materializer) {
@@ -386,4 +398,40 @@ public final class ModdedChunkUpdateService implements ModdedTickableService {
NativeWorldMaintenance.refresh(level, pos);
}
}
private static final class StaleCarriers {
private static final String UNCHECKED = "";
private final Map<String, String> carriers = new HashMap<>();
private int skipped;
private boolean carrierReplaced(String key, NativeBlockState live) {
String carrier = carriers.get(key);
if (carrier == null) {
carrier = carrierKey(key);
carriers.put(key, carrier);
}
if (carrier.equals(UNCHECKED) || carrier.equals(IrisProceduralBlocks.materialKey(live))) {
return false;
}
skipped++;
return true;
}
private void report(String levelName, int chunkX, int chunkZ) {
if (skipped == 0) {
return;
}
IrisLogging.debug("Skipped %d stale custom block records at chunk %d,%d in %s",
skipped, chunkX, chunkZ, levelName);
}
private static String carrierKey(String key) {
ModdedBlockData resolved = ModdedCustomContentRegistry.resolveBlock(key);
if (resolved == null || resolved.state().isAir()) {
return UNCHECKED;
}
return IrisProceduralBlocks.materialKey(resolved.state());
}
}
}
@@ -0,0 +1,205 @@
package art.arcane.iris.modded.service;
import art.arcane.iris.generation.mantle.EngineMantle;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.runtime.EngineMetrics;
import art.arcane.iris.integration.Identifier;
import art.arcane.iris.modded.api.ModdedBlockData;
import art.arcane.iris.modded.api.ModdedBlockPlacementContext;
import art.arcane.iris.modded.api.ModdedCustomContentRegistry;
import art.arcane.iris.modded.api.ModdedDataProvider;
import art.arcane.iris.modded.api.ModdedDataType;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.world.IrisWorld;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState;
import art.arcane.volmlib.nativelib.terrain.NativeWorld;
import art.arcane.volmlib.util.function.Consumer4;
import art.arcane.volmlib.util.mantle.flag.MantleFlag;
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 net.minecraft.SharedConstants;
import net.minecraft.core.BlockPos;
import net.minecraft.server.Bootstrap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.NoteBlock;
import net.minecraft.world.level.block.state.BlockState;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.MockedStatic;
import org.mockito.invocation.InvocationOnMock;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
public class ModdedChunkUpdateServiceCarrierTest {
private static final String MOD_ID = "iris_carrier_guard_test";
private static final String LAMP = MOD_ID + ":lamp";
private static final String CHAIR = MOD_ID + ":chair";
private static final List<BlockPos> PLACED = new CopyOnWriteArrayList<>();
@BeforeClass
public static void bootstrapMinecraftRegistries() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
ModdedCustomContentRegistry.register(new CarrierProvider());
}
@Before
public void clearPlacements() {
PLACED.clear();
}
@Test
public void staleRecordsAreSkippedWhileIntactAndAirCarrierRecordsArePlaced() {
RecordMantleChunk chunk = new RecordMantleChunk(List.of(
new Placed(1, LAMP),
new Placed(2, LAMP),
new Placed(3, LAMP),
new Placed(4, CHAIR)));
NativeWorld level = level(Map.of(
1, Blocks.NOTE_BLOCK.defaultBlockState().setValue(NoteBlock.NOTE, 7),
2, Blocks.AIR.defaultBlockState(),
3, Blocks.COBBLESTONE.defaultBlockState(),
4, Blocks.COBBLESTONE.defaultBlockState()));
List<String> debug = new ArrayList<>();
try (MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class)) {
logging.when(() -> IrisLogging.debug(anyString(), any(Object[].class))).thenAnswer((InvocationOnMock invocation) -> {
Object[] arguments = invocation.getArguments();
debug.add(String.format((String) arguments[0], Arrays.copyOfRange(arguments, 1, arguments.length)));
return null;
});
new ModdedChunkUpdateService().updateRegeneratedChunk(engine(chunk), level, 0, 0);
}
assertEquals(List.of(new BlockPos(1, 6, 1), new BlockPos(4, 6, 4)), PLACED);
assertTrue(chunk.isFlagged(MantleFlag.ETCHED));
assertTrue(chunk.customDeleted());
assertEquals(1, debug.size());
assertTrue(debug.getFirst(), debug.getFirst().contains("2 stale"));
}
private static NativeWorld level(Map<Integer, BlockState> live) {
ServerLevel serverLevel = mock(ServerLevel.class);
when(serverLevel.getBlockState(any(BlockPos.class))).thenReturn(Blocks.STONE.defaultBlockState());
NativeWorld level = mock(NativeWorld.class);
when(level.name()).thenReturn("example:carriers");
when(level.minHeight()).thenReturn(-64);
when(level.maxHeight()).thenReturn(320);
when(level.nativeHandle()).thenReturn(serverLevel);
for (Map.Entry<Integer, BlockState> entry : live.entrySet()) {
int offset = entry.getKey();
when(level.getBlock(offset, 6, offset)).thenReturn(ModdedBlockState.of(entry.getValue(), null));
}
return level;
}
private static Engine engine(RecordMantleChunk chunk) {
@SuppressWarnings("unchecked")
Mantle<Matter> mantle = mock(Mantle.class);
when(mantle.isChunkLoaded(0, 0)).thenReturn(true);
when(mantle.hasFlag(0, 0, MantleFlag.ETCHED)).thenAnswer((InvocationOnMock invocation) -> chunk.isFlagged(MantleFlag.ETCHED));
when(mantle.useChunk(0, 0)).thenAnswer((InvocationOnMock invocation) -> chunk.use());
IrisWorld world = mock(IrisWorld.class);
when(world.minHeight()).thenReturn(-64);
EngineMantle engineMantle = mock(EngineMantle.class);
when(engineMantle.getMantle()).thenReturn(mantle);
Engine engine = mock(Engine.class);
when(engine.getWorld()).thenReturn(world);
when(engine.getMetrics()).thenReturn(new EngineMetrics(8));
when(engine.getMantle()).thenReturn(engineMantle);
return engine;
}
@SuppressWarnings("unchecked")
private static MantleDataAdapter<Matter> emptyAdapter() {
return (MantleDataAdapter<Matter>) Proxy.newProxyInstance(
MantleDataAdapter.class.getClassLoader(),
new Class<?>[]{MantleDataAdapter.class},
(proxy, method, arguments) -> {
throw new UnsupportedOperationException(method.getName());
}
);
}
private record Placed(int offset, String key) {
}
private static final class RecordMantleChunk extends MantleChunk<Matter> {
private final List<Placed> records;
private boolean customDeleted;
private RecordMantleChunk(List<Placed> records) {
super(1, 0, 0, emptyAdapter(), MantleHooks.NONE);
this.records = records;
}
@Override
public <T> void iterate(Class<T> type, Consumer4<Integer, Integer, Integer, T> iterator) {
if (type != Identifier.class) {
return;
}
for (Placed record : records) {
iterator.accept(record.offset(), 70, record.offset(), type.cast(Identifier.fromString(record.key())));
}
}
@Override
public void deleteSlices(Class<?> type) {
if (type == Identifier.class) {
customDeleted = true;
}
}
private boolean customDeleted() {
return customDeleted;
}
}
private static final class CarrierProvider implements ModdedDataProvider {
@Override
public String modId() {
return MOD_ID;
}
@Override
public Collection<String> getTypes(ModdedDataType type) {
return type == ModdedDataType.BLOCK ? List.of(LAMP, CHAIR) : List.of();
}
@Override
public boolean isValidProvider(String id, ModdedDataType type) {
return type == ModdedDataType.BLOCK && (LAMP.equals(id) || CHAIR.equals(id));
}
@Override
public ModdedBlockData getBlockData(String blockId, Map<String, String> state) {
BlockState carrier = CHAIR.equals(blockId) ? Blocks.AIR.defaultBlockState() : Blocks.NOTE_BLOCK.defaultBlockState();
return ModdedBlockData.deferred(ModdedBlockState.of(carrier, null));
}
@Override
public void processBlockPlacement(ModdedBlockPlacementContext context) {
PLACED.add(context.placement().position());
}
}
}
@@ -136,6 +136,12 @@
"replacement": "when(level.getMaxBuildHeight()).thenReturn(320);"
}
],
"art/arcane/iris/modded/service/ModdedChunkUpdateServiceCarrierTest.java": [
{
"pattern": "\\bResourceLocation\\b",
"replacement": "Identifier"
}
],
"art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeSpawnPopulationTest.java": [
{
"pattern": "when\\(level\\.getMaxY\\(\\)\\)\\.thenReturn\\(320\\);",