mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 22:10:18 +00:00
Platform glue diet
- Chunk-scoped visible biome resolver without global cache, per-quart RNG or repeated runtime lookups - Subterrain probe skips per-sample subterrain lookups in chunks without candidates - Striped structure volume caches - Saved biome store reuses deflaters and remembers ready region files - Semantic journal skips repeated storage and journal stat checks
This commit is contained in:
1 parent
af90765a92
commit
0fe2363feb
12 files changed
+450
-115
No files matched your search
+111
@@ -0,0 +1,111 @@
|
||||
package art.arcane.iris.platform.bukkit.nms;
|
||||
|
||||
import art.arcane.iris.generation.biome.IrisBiome;
|
||||
import art.arcane.iris.generation.biome.IrisBiomeCustom;
|
||||
import art.arcane.iris.generation.mantle.EngineMantle;
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.generation.runtime.GenerationSessionLease;
|
||||
import art.arcane.iris.generation.runtime.IrisComplex;
|
||||
import art.arcane.iris.generation.runtime.IrisEngine;
|
||||
import art.arcane.iris.generation.terrain.IrisDimension;
|
||||
import art.arcane.iris.generation.terrain.IrisDimensionCarvingResolver;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.iris.world.IrisWorld;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBiomeRegistry;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBiomeSourcePolicy;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Answers.CALLS_REAL_METHODS;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BukkitBiomeVisibleResolverTest {
|
||||
@Test
|
||||
public void chunkResolverMatchesThePerQuartResolution() throws Exception {
|
||||
IrisBiome surface = customBiome("surface", "meadow");
|
||||
IrisBiome cave = customBiome("cave", "grotto");
|
||||
IrisBiome custom = customBiome("custom", "mirage");
|
||||
IrisBiome feature = customBiome("feature", "bloom");
|
||||
IrisDimension dimension = mock(IrisDimension.class);
|
||||
when(dimension.getReachableBiomes(any())).thenReturn(new KList<>(surface, cave, custom, feature));
|
||||
when(dimension.getCustomBiomeKey(anyString())).thenAnswer(invocation -> "pack:" + invocation.getArgument(0));
|
||||
IrisData data = mock(IrisData.class);
|
||||
when(data.customBiomeResourceKey(any(IrisDimension.class), anyString()))
|
||||
.thenAnswer(invocation -> "pack:" + invocation.getArgument(1));
|
||||
IrisComplex complex = mock(IrisComplex.class);
|
||||
when(complex.historicalPhysicalBiomeKeyAt(anyInt(), anyInt(), anyInt())).thenReturn(Optional.empty());
|
||||
EngineMantle mantle = mock(EngineMantle.class);
|
||||
when(mantle.trueHeight(anyInt(), anyInt())).thenAnswer(invocation ->
|
||||
120 + Math.floorMod(invocation.<Integer>getArgument(0) * 7 + invocation.<Integer>getArgument(1) * 3, 160));
|
||||
IrisEngine engine = mock(IrisEngine.class);
|
||||
when(engine.getDimension()).thenReturn(dimension);
|
||||
when(engine.getData()).thenReturn(data);
|
||||
when(engine.getComplex()).thenReturn(complex);
|
||||
when(engine.getMantle()).thenReturn(mantle);
|
||||
when(engine.getWorld()).thenReturn(IrisWorld.builder().minHeight(-64).maxHeight(320).build());
|
||||
when(engine.getMinHeight()).thenReturn(-64);
|
||||
when(engine.getCacheID()).thenReturn(1);
|
||||
when(engine.acquireGenerationLease(anyString())).thenReturn(GenerationSessionLease.noop());
|
||||
when(engine.answersFromNaturalTerrain(anyInt(), anyInt())).thenReturn(false);
|
||||
when(engine.getHostSurfaceBiome(anyInt(), anyInt())).thenAnswer(invocation ->
|
||||
Math.floorMod(invocation.<Integer>getArgument(0) + invocation.<Integer>getArgument(1), 8) == 0 ? custom : surface);
|
||||
when(engine.getCaveBiome(anyInt(), anyInt(), anyInt(), any(IrisDimensionCarvingResolver.State.class)))
|
||||
.thenAnswer(invocation -> invocation.<Integer>getArgument(1) % 3 == 0 ? custom : cave);
|
||||
when(engine.getSubterrainBiome(anyInt(), anyInt(), anyInt())).thenAnswer(invocation ->
|
||||
invocation.<Integer>getArgument(0) == 4 && invocation.<Integer>getArgument(1) == 40 ? feature : null);
|
||||
when(engine.subterrainAbsentInChunk(1, 0)).thenReturn(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
NativeBiomeRegistry<String, String> registry = mock(NativeBiomeRegistry.class);
|
||||
when(registry.lookupCustom(anyString())).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
BukkitBiomePolicy<String, String> policy = policy(engine, registry, "fallback");
|
||||
|
||||
for (int chunkX = 0; chunkX <= 1; chunkX++) {
|
||||
NativeBiomeSourcePolicy.VisibleResolver<String> resolver = policy.visibleResolver();
|
||||
for (int quartY = -16; quartY < 80; quartY++) {
|
||||
for (int localX = 0; localX < 4; localX++) {
|
||||
for (int localZ = 0; localZ < 4; localZ++) {
|
||||
int quartX = (chunkX << 2) + localX;
|
||||
String expected = policy.getVisibleNoiseBiomeWithActiveGenerationLease(
|
||||
quartX, quartY, localZ, new IrisDimensionCarvingResolver.State());
|
||||
assertEquals("quart " + quartX + "," + quartY + "," + localZ, expected,
|
||||
resolver.biome(quartX, quartY, localZ));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IrisBiome customBiome(String key, String customId) {
|
||||
IrisBiome biome = new IrisBiome().setCustomDerivitives(new KList<>(new IrisBiomeCustom().setId(customId)));
|
||||
biome.setLoadKey(key);
|
||||
return biome;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static BukkitBiomePolicy<String, String> policy(Engine engine, NativeBiomeRegistry<String, String> registry,
|
||||
String fallback) throws Exception {
|
||||
BukkitBiomePolicy<String, String> policy = mock(BukkitBiomePolicy.class, CALLS_REAL_METHODS);
|
||||
set(policy, "engine", engine);
|
||||
set(policy, "biomeCustomRegistry", registry);
|
||||
set(policy, "biomeRegistry", registry);
|
||||
set(policy, "fallbackBiome", fallback);
|
||||
set(policy, "runtimeBiomeStates", new ConcurrentHashMap<>());
|
||||
set(policy, "noiseBiomeCache", new ConcurrentHashMap<>());
|
||||
return policy;
|
||||
}
|
||||
|
||||
private static void set(Object target, String name, Object value) throws Exception {
|
||||
Field field = BukkitBiomePolicy.class.getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
field.set(target, value);
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -36,10 +36,12 @@ public class BukkitSubterrainBiomePrecedenceTest {
|
||||
owner.setAccessible(true);
|
||||
owner.set(policy, engine);
|
||||
DimensionStackLayout stack = mock(DimensionStackLayout.class);
|
||||
Class<?> chunkResolution = Class.forName(BukkitBiomePolicy.class.getName() + "$ChunkResolution");
|
||||
Method resolve = BukkitBiomePolicy.class.getDeclaredMethod("resolveBiomeResolution", int.class, int.class,
|
||||
int.class, IrisDimensionCarvingResolver.State.class, boolean.class, DimensionStackLayout.class);
|
||||
int.class, IrisDimensionCarvingResolver.State.class, boolean.class, DimensionStackLayout.class,
|
||||
chunkResolution);
|
||||
resolve.setAccessible(true);
|
||||
Object resolved = resolve.invoke(policy, 3, 40, -5, null, true, stack);
|
||||
Object resolved = resolve.invoke(policy, 3, 40, -5, null, true, stack, null);
|
||||
assertSame(feature, field(resolved, "irisBiome"));
|
||||
assertSame(host, field(resolved, "dimension"));
|
||||
assertEquals(true, field(resolved, "underground"));
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package art.arcane.iris.structure.nativegen;
|
||||
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class NativeStructureVolumeIndexCacheTest {
|
||||
@Test
|
||||
public void concurrentQueriesBuildEachOriginOnceAndAnswerIdentically() throws Exception {
|
||||
Map<Long, AtomicInteger> builds = new ConcurrentHashMap<>();
|
||||
NativeStructureVolumeIndex index = NativeStructureVolumeIndex.forTesting((engine, chunkX, chunkZ) -> {
|
||||
builds.computeIfAbsent(((long) chunkX << 32) ^ (chunkZ & 0xffffffffL), key -> new AtomicInteger()).incrementAndGet();
|
||||
return volumesAt(chunkX, chunkZ);
|
||||
});
|
||||
Engine engine = mock(Engine.class);
|
||||
when(engine.getCacheID()).thenReturn(7);
|
||||
KList<NativeStructureVolume> expected = NativeStructureVolumeIndex.forTesting(
|
||||
(ignored, chunkX, chunkZ) -> volumesAt(chunkX, chunkZ)).resolve(engine, 0, 0, 47, 47);
|
||||
ExecutorService pool = Executors.newFixedThreadPool(8);
|
||||
try {
|
||||
List<Future<KList<NativeStructureVolume>>> results = new ArrayList<>();
|
||||
for (int task = 0; task < 32; task++) {
|
||||
results.add(pool.submit(() -> index.resolve(engine, 0, 0, 47, 47)));
|
||||
}
|
||||
for (Future<KList<NativeStructureVolume>> result : results) {
|
||||
assertEquals(expected, result.get(30, TimeUnit.SECONDS));
|
||||
}
|
||||
} finally {
|
||||
pool.shutdown();
|
||||
assertTrue(pool.awaitTermination(30, TimeUnit.SECONDS));
|
||||
}
|
||||
for (AtomicInteger count : builds.values()) {
|
||||
assertEquals(1, count.get());
|
||||
}
|
||||
int distinct = builds.size();
|
||||
index.evictRuntime(7);
|
||||
assertEquals(expected, index.resolve(engine, 0, 0, 47, 47));
|
||||
assertEquals(distinct, builds.size());
|
||||
for (AtomicInteger count : builds.values()) {
|
||||
assertEquals(2, count.get());
|
||||
}
|
||||
}
|
||||
|
||||
private static KList<NativeStructureVolume> volumesAt(int chunkX, int chunkZ) {
|
||||
if (Math.floorMod(chunkX * 31 + chunkZ * 17, 5) != 0) {
|
||||
return NativeStructureVolume.NONE;
|
||||
}
|
||||
int minX = chunkX << 4;
|
||||
int minZ = chunkZ << 4;
|
||||
return new KList<>(new NativeStructureVolume("test:" + chunkX + "_" + chunkZ,
|
||||
minX - 20, -10, minZ - 20, minX + 36, 40, minZ + 36, null));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user