mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-11 19:14:11 +00:00
Deferred saved biome updates
- Defers a chunk update pass while saved biome information is loading and retries it on a later pass - Forgets only the updates already applied so a retry never refills a chest - Propagates engine failures from loot application instead of marking the chunk done
This commit is contained in:
+39
-5
@@ -30,6 +30,7 @@ import art.arcane.iris.studio.view.PregeneratorJob;
|
||||
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.runtime.GenerationFailures;
|
||||
import art.arcane.iris.generation.block.TileData;
|
||||
import art.arcane.iris.modded.IrisModdedChunkGenerator;
|
||||
import art.arcane.iris.modded.ModdedBlockResolution;
|
||||
@@ -38,6 +39,7 @@ import art.arcane.iris.modded.ModdedTileData;
|
||||
import art.arcane.iris.modded.api.ModdedCustomContentRegistry;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockPlacement;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.world.history.SavedBiomeUnavailableException;
|
||||
import art.arcane.iris.world.storage.matter.TileWrapper;
|
||||
import art.arcane.volmlib.util.mantle.flag.MantleFlag;
|
||||
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||
@@ -47,6 +49,7 @@ import art.arcane.volmlib.util.matter.MatterCavern;
|
||||
import art.arcane.volmlib.util.matter.MatterUpdate;
|
||||
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.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -195,6 +198,11 @@ public final class ModdedChunkUpdateService implements ModdedTickableService {
|
||||
chunk.raiseFlagUnchecked(MantleFlag.CUSTOM, () -> runCustomPass(engine, level, chunkX, chunkZ, chunk));
|
||||
chunk.raiseFlagUnchecked(MantleFlag.UPDATE, () -> runUpdatePass(engine, level, chunkX, chunkZ, chunk));
|
||||
});
|
||||
} catch (SavedBiomeUnavailableException unavailable) {
|
||||
if (!unavailable.isLoading() || unavailable.getSuppressed().length != 0) {
|
||||
throw unavailable;
|
||||
}
|
||||
IrisLogging.debug("Iris chunk update deferred at " + chunkX + "," + chunkZ + ": saved biomes loading");
|
||||
} finally {
|
||||
chunk.release();
|
||||
}
|
||||
@@ -315,15 +323,40 @@ public final class ModdedChunkUpdateService implements ModdedTickableService {
|
||||
}
|
||||
}
|
||||
|
||||
chunk.iterate(MatterUpdate.class, (Integer x, Integer yf, Integer z, MatterUpdate v) -> {
|
||||
if (v != null && v.isUpdate()) {
|
||||
update(engine, level, x, yf + minHeight, z, baseX, baseZ, chunk);
|
||||
}
|
||||
});
|
||||
IntArrayList completed = new IntArrayList();
|
||||
try {
|
||||
chunk.iterate(MatterUpdate.class, (Integer x, Integer yf, Integer z, MatterUpdate v) -> {
|
||||
if (v != null && v.isUpdate()) {
|
||||
update(engine, level, x, yf + minHeight, z, baseX, baseZ, chunk);
|
||||
completed.add((yf << 8) | ((x & 15) << 4) | (z & 15));
|
||||
}
|
||||
});
|
||||
} catch (RuntimeException | Error failure) {
|
||||
forgetCompletedUpdates(chunk, completed, failure);
|
||||
throw failure;
|
||||
}
|
||||
chunk.deleteSlices(MatterUpdate.class);
|
||||
engine.getMetrics().getUpdates().put(stopwatch.getMilliseconds());
|
||||
}
|
||||
|
||||
private static void forgetCompletedUpdates(MantleChunk<Matter> chunk, IntArrayList completed, Throwable failure) {
|
||||
try {
|
||||
for (int index = 0; index < completed.size(); index++) {
|
||||
int position = completed.getInt(index);
|
||||
int y = position >>> 8;
|
||||
Matter section = chunk.get(y >> 4);
|
||||
if (section != null && section.hasSlice(MatterUpdate.class)) {
|
||||
section.<MatterUpdate>getSlice(MatterUpdate.class)
|
||||
.set((position >> 4) & 15, y & 15, position & 15, null);
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException | Error cleanupFailure) {
|
||||
if (cleanupFailure != failure) {
|
||||
failure.addSuppressed(cleanupFailure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void update(Engine engine, NativeWorld level, int x, int y, int z, int baseX, int baseZ, MantleChunk<Matter> chunk) {
|
||||
if (y < level.minHeight() || y >= level.maxHeight() - 1) {
|
||||
return;
|
||||
@@ -338,6 +371,7 @@ public final class ModdedChunkUpdateService implements ModdedTickableService {
|
||||
try {
|
||||
ModdedLootApplier.apply(engine, level, pos, state, chunk);
|
||||
} catch (Throwable e) {
|
||||
GenerationFailures.rethrowEngineFailure(e);
|
||||
IrisLogging.reportError(e);
|
||||
}
|
||||
} else {
|
||||
|
||||
+88
@@ -4,13 +4,17 @@ import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedPlatformWorld;
|
||||
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.generation.runtime.EngineMetrics;
|
||||
import art.arcane.iris.modded.ModdedLootApplier;
|
||||
import art.arcane.iris.world.IrisWorld;
|
||||
import art.arcane.iris.world.history.SavedBiomeUnavailableException;
|
||||
import art.arcane.iris.world.storage.matter.TileWrapper;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint;
|
||||
import art.arcane.volmlib.util.function.Consumer4;
|
||||
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 art.arcane.volmlib.util.matter.MatterSlice;
|
||||
import art.arcane.volmlib.util.matter.MatterUpdate;
|
||||
import art.arcane.volmlib.util.matter.slices.UpdateMatter;
|
||||
import net.minecraft.SharedConstants;
|
||||
@@ -24,6 +28,7 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.FlowingFluid;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
@@ -31,12 +36,17 @@ import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
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.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
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 ModdedChunkUpdateServiceTest {
|
||||
@@ -70,6 +80,44 @@ public class ModdedChunkUpdateServiceTest {
|
||||
assertTrue(chunk.deleted());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadingSavedBiomeDefersThePassAndForgetsOnlyCompletedLoot() {
|
||||
SharedConstants.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
IrisWorld world = mock(IrisWorld.class);
|
||||
when(world.minHeight()).thenReturn(-64);
|
||||
Engine engine = mock(Engine.class);
|
||||
when(engine.getWorld()).thenReturn(world);
|
||||
when(engine.getMetrics()).thenReturn(new EngineMetrics(8));
|
||||
ServerLevel level = mock(ServerLevel.class);
|
||||
when(level.getMinY()).thenReturn(-64);
|
||||
when(level.getMaxY()).thenReturn(319);
|
||||
when(level.getHeight()).thenReturn(384);
|
||||
when(level.getBlockState(any(BlockPos.class))).thenReturn(Blocks.CHEST.defaultBlockState());
|
||||
ChestUpdateMantleChunk chunk = new ChestUpdateMantleChunk();
|
||||
SavedBiomeUnavailableException loading = new SavedBiomeUnavailableException(
|
||||
"Saved biome information is loading at chunk 0,0. Try again shortly.", true);
|
||||
|
||||
try (MockedStatic<ModdedLootApplier> loot = mockStatic(ModdedLootApplier.class)) {
|
||||
loot.when(() -> ModdedLootApplier.apply(any(), any(), any(), any(), any())).thenAnswer(invocation -> {
|
||||
NativeBlockPoint position = invocation.getArgument(2);
|
||||
if (position.x() == 2) {
|
||||
throw loading;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
SavedBiomeUnavailableException thrown = assertThrows(SavedBiomeUnavailableException.class,
|
||||
() -> new ModdedChunkUpdateService().runUpdatePass(
|
||||
engine, new ModdedPlatformWorld(level), 0, 0, chunk));
|
||||
|
||||
assertSame(loading, thrown);
|
||||
}
|
||||
assertFalse(chunk.deleted());
|
||||
verify(chunk.slice).set(1, 5, 1, null);
|
||||
verify(chunk.slice, never()).set(2, 5, 2, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scansWhenPlayersArePresent() {
|
||||
assertTrue(ModdedChunkUpdateService.hasUpdateTargets(true, false));
|
||||
@@ -166,6 +214,46 @@ public class ModdedChunkUpdateServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ChestUpdateMantleChunk extends MantleChunk<Matter> {
|
||||
@SuppressWarnings("unchecked")
|
||||
private final MatterSlice<MatterUpdate> slice = mock(MatterSlice.class);
|
||||
private final Matter section = mock(Matter.class);
|
||||
private boolean deleted;
|
||||
|
||||
private ChestUpdateMantleChunk() {
|
||||
super(1, 0, 0, emptyAdapter(), MantleHooks.NONE);
|
||||
when(section.hasSlice(MatterUpdate.class)).thenReturn(true);
|
||||
when(section.getSlice(MatterUpdate.class)).thenReturn(slice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matter get(int index) {
|
||||
return section;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void iterate(
|
||||
Class<T> type,
|
||||
Consumer4<Integer, Integer, Integer, T> iterator
|
||||
) {
|
||||
if (type == MatterUpdate.class) {
|
||||
iterator.accept(1, 5, 1, type.cast(UpdateMatter.ON));
|
||||
iterator.accept(2, 5, 2, type.cast(UpdateMatter.ON));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSlices(Class<?> type) {
|
||||
if (type == MatterUpdate.class) {
|
||||
deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean deleted() {
|
||||
return deleted;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static MantleDataAdapter<Matter> emptyAdapter() {
|
||||
return (MantleDataAdapter<Matter>) Proxy.newProxyInstance(
|
||||
|
||||
Reference in New Issue
Block a user