mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-12 03:24:14 +00:00
Poplar
This commit is contained in:
+66
@@ -1895,6 +1895,72 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(raised, 0, 68, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loweredSnowSurfaceRemovesSolidSlabDecoration() {
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
for (int y = 82; y < 99; y++) {
|
||||
put(blocks, 0, y, 0, Blocks.STONE.defaultBlockState());
|
||||
}
|
||||
put(blocks, 0, 99, 0, Blocks.SNOW_BLOCK.defaultBlockState());
|
||||
put(blocks, 0, 100, 0, Blocks.COBBLESTONE_SLAB.defaultBlockState());
|
||||
|
||||
NativeStructureSurfaceFitter.applySurfaceColumn(
|
||||
world(blocks), new BlockPos.MutableBlockPos(),
|
||||
0, 0, 99, 83, -64, 319);
|
||||
|
||||
assertEquals(Blocks.STONE.defaultBlockState(), state(blocks, 0, 82, 0));
|
||||
assertEquals(Blocks.SNOW_BLOCK.defaultBlockState(), state(blocks, 0, 83, 0));
|
||||
for (int y = 84; y <= 100; y++) {
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, y, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loweredSurfaceRemovesStackedSolidDecorationUntilAir() {
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
put(blocks, 0, 62, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, 0, 63, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, 0, 64, 0, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
put(blocks, 0, 65, 0, Blocks.COBBLESTONE.defaultBlockState());
|
||||
put(blocks, 0, 66, 0, Blocks.COBBLESTONE_STAIRS.defaultBlockState());
|
||||
put(blocks, 0, 67, 0, Blocks.COBBLESTONE_WALL.defaultBlockState());
|
||||
put(blocks, 0, 69, 0, Blocks.STONE.defaultBlockState());
|
||||
|
||||
NativeStructureSurfaceFitter.applySurfaceColumn(
|
||||
world(blocks), new BlockPos.MutableBlockPos(),
|
||||
0, 0, 64, 62, -64, 319);
|
||||
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 0, 62, 0));
|
||||
for (int y = 63; y <= 68; y++) {
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, y, 0));
|
||||
}
|
||||
assertEquals(Blocks.STONE.defaultBlockState(), state(blocks, 0, 69, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loweredSurfacePreservesTreeDecorationBoundary() {
|
||||
for (BlockState tree : List.of(Blocks.OAK_LOG.defaultBlockState(), Blocks.OAK_LEAVES.defaultBlockState())) {
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
put(blocks, 0, 62, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, 0, 63, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, 0, 64, 0, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
put(blocks, 0, 65, 0, Blocks.COBBLESTONE_SLAB.defaultBlockState());
|
||||
put(blocks, 0, 66, 0, tree);
|
||||
put(blocks, 0, 67, 0, Blocks.STONE.defaultBlockState());
|
||||
|
||||
NativeStructureSurfaceFitter.applySurfaceColumn(
|
||||
world(blocks), new BlockPos.MutableBlockPos(),
|
||||
0, 0, 64, 62, -64, 319);
|
||||
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 0, 62, 0));
|
||||
for (int y = 63; y <= 65; y++) {
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, y, 0));
|
||||
}
|
||||
assertEquals(tree, state(blocks, 0, 66, 0));
|
||||
assertEquals(Blocks.STONE.defaultBlockState(), state(blocks, 0, 67, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rigidSurfaceBaseClosesAOneBlockSubsurfaceGap() {
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
|
||||
+30
-2
@@ -239,8 +239,8 @@ public class NativeStructurePostProcessorVegetationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loweringSurfaceKeepsTreeAndSolidObjectBoundaries() {
|
||||
for (BlockState boundary : List.of(Blocks.OAK_LOG.defaultBlockState(), Blocks.OAK_PLANKS.defaultBlockState())) {
|
||||
public void loweringSurfaceKeepsTreeBoundaries() {
|
||||
for (BlockState boundary : List.of(Blocks.OAK_LOG.defaultBlockState(), Blocks.OAK_LEAVES.defaultBlockState())) {
|
||||
ProtoChunk chunk = chunk();
|
||||
write(chunk, 4, 63, 4, Blocks.SAND.defaultBlockState());
|
||||
write(chunk, 4, 64, 4, Blocks.SAND.defaultBlockState());
|
||||
@@ -257,6 +257,34 @@ public class NativeStructurePostProcessorVegetationTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loweringSurfaceRemovesUnsupportedPlankDecoration() {
|
||||
ProtoChunk chunk = chunk();
|
||||
for (int x : List.of(4, 5)) {
|
||||
for (int y = 60; y <= 64; y++) {
|
||||
write(chunk, x, y, 4, Blocks.SAND.defaultBlockState());
|
||||
}
|
||||
write(chunk, x, 65, 4, Blocks.OAK_PLANKS.defaultBlockState());
|
||||
write(chunk, x, 66, 4, Blocks.CACTUS.defaultBlockState());
|
||||
write(chunk, x, 67, 4, Blocks.CACTUS_FLOWER.defaultBlockState());
|
||||
}
|
||||
WorldGenLevel world = world(chunk);
|
||||
|
||||
NativeStructureSurfaceFitter.applySurfaceColumn(
|
||||
world, new BlockPos.MutableBlockPos(), 4, 4, 64, 60, MIN_Y, MAX_Y);
|
||||
NativeStructureSurfaceFitter.applySurfaceColumn(
|
||||
world, new BlockPos.MutableBlockPos(), 5, 4, 64, 64, MIN_Y, MAX_Y);
|
||||
|
||||
assertEquals(Blocks.SAND.defaultBlockState(), chunk.getBlockState(new BlockPos(4, 60, 4)));
|
||||
for (int y = 61; y <= 67; y++) {
|
||||
assertTrue(chunk.getBlockState(new BlockPos(4, y, 4)).isAir());
|
||||
}
|
||||
assertEquals(Blocks.SAND.defaultBlockState(), chunk.getBlockState(new BlockPos(5, 64, 4)));
|
||||
assertEquals(Blocks.OAK_PLANKS.defaultBlockState(), chunk.getBlockState(new BlockPos(5, 65, 4)));
|
||||
assertEquals(Blocks.CACTUS.defaultBlockState(), chunk.getBlockState(new BlockPos(5, 66, 4)));
|
||||
assertEquals(Blocks.CACTUS_FLOWER.defaultBlockState(), chunk.getBlockState(new BlockPos(5, 67, 4)));
|
||||
}
|
||||
|
||||
private static void clearVegetation(ProtoChunk chunk, StructureStart start) {
|
||||
NativeStructureVegetationClearer.clearIntersectingVegetation(
|
||||
world(chunk), chunk, AREA, List.of(start));
|
||||
|
||||
+66
@@ -1895,6 +1895,72 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(raised, 0, 68, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loweredSnowSurfaceRemovesSolidSlabDecoration() {
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
for (int y = 82; y < 99; y++) {
|
||||
put(blocks, 0, y, 0, Blocks.STONE.defaultBlockState());
|
||||
}
|
||||
put(blocks, 0, 99, 0, Blocks.SNOW_BLOCK.defaultBlockState());
|
||||
put(blocks, 0, 100, 0, Blocks.COBBLESTONE_SLAB.defaultBlockState());
|
||||
|
||||
NativeStructureSurfaceFitter.applySurfaceColumn(
|
||||
world(blocks), new BlockPos.MutableBlockPos(),
|
||||
0, 0, 99, 83, -64, 319);
|
||||
|
||||
assertEquals(Blocks.STONE.defaultBlockState(), state(blocks, 0, 82, 0));
|
||||
assertEquals(Blocks.SNOW_BLOCK.defaultBlockState(), state(blocks, 0, 83, 0));
|
||||
for (int y = 84; y <= 100; y++) {
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, y, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loweredSurfaceRemovesStackedSolidDecorationUntilAir() {
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
put(blocks, 0, 62, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, 0, 63, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, 0, 64, 0, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
put(blocks, 0, 65, 0, Blocks.COBBLESTONE.defaultBlockState());
|
||||
put(blocks, 0, 66, 0, Blocks.COBBLESTONE_STAIRS.defaultBlockState());
|
||||
put(blocks, 0, 67, 0, Blocks.COBBLESTONE_WALL.defaultBlockState());
|
||||
put(blocks, 0, 69, 0, Blocks.STONE.defaultBlockState());
|
||||
|
||||
NativeStructureSurfaceFitter.applySurfaceColumn(
|
||||
world(blocks), new BlockPos.MutableBlockPos(),
|
||||
0, 0, 64, 62, -64, 319);
|
||||
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 0, 62, 0));
|
||||
for (int y = 63; y <= 68; y++) {
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, y, 0));
|
||||
}
|
||||
assertEquals(Blocks.STONE.defaultBlockState(), state(blocks, 0, 69, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loweredSurfacePreservesTreeDecorationBoundary() {
|
||||
for (BlockState tree : List.of(Blocks.OAK_LOG.defaultBlockState(), Blocks.OAK_LEAVES.defaultBlockState())) {
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
put(blocks, 0, 62, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, 0, 63, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, 0, 64, 0, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
put(blocks, 0, 65, 0, Blocks.COBBLESTONE_SLAB.defaultBlockState());
|
||||
put(blocks, 0, 66, 0, tree);
|
||||
put(blocks, 0, 67, 0, Blocks.STONE.defaultBlockState());
|
||||
|
||||
NativeStructureSurfaceFitter.applySurfaceColumn(
|
||||
world(blocks), new BlockPos.MutableBlockPos(),
|
||||
0, 0, 64, 62, -64, 319);
|
||||
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 0, 62, 0));
|
||||
for (int y = 63; y <= 65; y++) {
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, y, 0));
|
||||
}
|
||||
assertEquals(tree, state(blocks, 0, 66, 0));
|
||||
assertEquals(Blocks.STONE.defaultBlockState(), state(blocks, 0, 67, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rigidSurfaceBaseClosesAOneBlockSubsurfaceGap() {
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
|
||||
+30
-2
@@ -239,8 +239,8 @@ public class NativeStructurePostProcessorVegetationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loweringSurfaceKeepsTreeAndSolidObjectBoundaries() {
|
||||
for (BlockState boundary : List.of(Blocks.OAK_LOG.defaultBlockState(), Blocks.OAK_PLANKS.defaultBlockState())) {
|
||||
public void loweringSurfaceKeepsTreeBoundaries() {
|
||||
for (BlockState boundary : List.of(Blocks.OAK_LOG.defaultBlockState(), Blocks.OAK_LEAVES.defaultBlockState())) {
|
||||
ProtoChunk chunk = chunk();
|
||||
write(chunk, 4, 63, 4, Blocks.SAND.defaultBlockState());
|
||||
write(chunk, 4, 64, 4, Blocks.SAND.defaultBlockState());
|
||||
@@ -257,6 +257,34 @@ public class NativeStructurePostProcessorVegetationTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loweringSurfaceRemovesUnsupportedPlankDecoration() {
|
||||
ProtoChunk chunk = chunk();
|
||||
for (int x : List.of(4, 5)) {
|
||||
for (int y = 60; y <= 64; y++) {
|
||||
write(chunk, x, y, 4, Blocks.SAND.defaultBlockState());
|
||||
}
|
||||
write(chunk, x, 65, 4, Blocks.OAK_PLANKS.defaultBlockState());
|
||||
write(chunk, x, 66, 4, Blocks.CACTUS.defaultBlockState());
|
||||
write(chunk, x, 67, 4, Blocks.CACTUS_FLOWER.defaultBlockState());
|
||||
}
|
||||
WorldGenLevel world = world(chunk);
|
||||
|
||||
NativeStructureSurfaceFitter.applySurfaceColumn(
|
||||
world, new BlockPos.MutableBlockPos(), 4, 4, 64, 60, MIN_Y, MAX_Y);
|
||||
NativeStructureSurfaceFitter.applySurfaceColumn(
|
||||
world, new BlockPos.MutableBlockPos(), 5, 4, 64, 64, MIN_Y, MAX_Y);
|
||||
|
||||
assertEquals(Blocks.SAND.defaultBlockState(), chunk.getBlockState(new BlockPos(4, 60, 4)));
|
||||
for (int y = 61; y <= 67; y++) {
|
||||
assertTrue(chunk.getBlockState(new BlockPos(4, y, 4)).isAir());
|
||||
}
|
||||
assertEquals(Blocks.SAND.defaultBlockState(), chunk.getBlockState(new BlockPos(5, 64, 4)));
|
||||
assertEquals(Blocks.OAK_PLANKS.defaultBlockState(), chunk.getBlockState(new BlockPos(5, 65, 4)));
|
||||
assertEquals(Blocks.CACTUS.defaultBlockState(), chunk.getBlockState(new BlockPos(5, 66, 4)));
|
||||
assertEquals(Blocks.CACTUS_FLOWER.defaultBlockState(), chunk.getBlockState(new BlockPos(5, 67, 4)));
|
||||
}
|
||||
|
||||
private static void clearVegetation(ProtoChunk chunk, StructureStart start) {
|
||||
NativeStructureVegetationClearer.clearIntersectingVegetation(
|
||||
world(chunk), chunk, AREA, List.of(start));
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
export default {
|
||||
name: 'iris-dust-revealer',
|
||||
description: 'Inspect generated terrain with Dust of Revealing and retry pending saved-biome reads.',
|
||||
async run(context) {
|
||||
const bot = context.bot;
|
||||
const returnPosition = bot.entity.position.clone();
|
||||
await context.step('create an Iris terrain world', async () => {
|
||||
const response = await context.command('/irislifecycle terrain-create', /NATIVE_TERRAIN seed=|NATIVE_FAILED/, 180000);
|
||||
context.expect(response.includes('NATIVE_TERRAIN seed=78264193 columns=16 biome=plains'), response);
|
||||
});
|
||||
await context.step('enter the generated terrain and receive reveal dust', async () => {
|
||||
await context.command(`/execute in iris:native_terrain_probe run tp ${bot.username} 34 101 -46`, /Teleported/, 10000);
|
||||
await context.waitForEvent('physicsTick', () => bot.entity.onGround && Math.abs(bot.entity.position.x - 34) < 1, 15000);
|
||||
const received = context.waitForEvent('physicsTick', () => bot.inventory.items().some(item => item.name === 'glowstone_dust'), 10000);
|
||||
await context.command('/ir o d');
|
||||
await received;
|
||||
const dust = bot.inventory.items().find(item => item.name === 'glowstone_dust');
|
||||
context.expect(dust !== undefined, 'Dust command did not grant glowstone dust');
|
||||
await bot.equip(dust, 'hand');
|
||||
});
|
||||
for (let inspection = 0; inspection < 2; inspection++) {
|
||||
await context.step(`inspect the block with reveal dust ${inspection + 1}`, async () => {
|
||||
const target = bot.findBlock({ matching: block => block.name === 'lime_wool', maxDistance: 4 });
|
||||
context.expect(target !== null, 'Generated terrain block is not within reach');
|
||||
for (let attempt = 0; attempt < 10; attempt++) {
|
||||
const start = context.report.messages.length;
|
||||
const response = context.waitForMessage(/Saved biome information is loading|Click to copy these stats|Object reveal failed/, 10000);
|
||||
await bot.activateBlock(target);
|
||||
const message = await response;
|
||||
if (message.includes('Saved biome information is loading')) {
|
||||
await context.sleep(100);
|
||||
continue;
|
||||
}
|
||||
context.expect(message.includes('Click to copy these stats'), message);
|
||||
const messages = context.report.messages.slice(start).map(entry => entry.message).join('\n');
|
||||
context.expect(/Surface biome: flat/.test(messages), messages);
|
||||
context.expect(/Region: flat/.test(messages), messages);
|
||||
return;
|
||||
}
|
||||
context.expect(false, 'Saved biome information remained loading after ten inspections');
|
||||
});
|
||||
}
|
||||
await context.step('leave and unload the inspected world', async () => {
|
||||
await context.command(`/execute in minecraft:overworld run tp ${bot.username} ${returnPosition.x} ${returnPosition.y} ${returnPosition.z}`, /Teleported/, 10000);
|
||||
const response = await context.command('/irislifecycle terrain-unload', /NATIVE_TERRAIN_UNLOADED|NATIVE_FAILED/, 90000);
|
||||
context.expect(response.includes('NATIVE_TERRAIN_UNLOADED true'), response);
|
||||
});
|
||||
}
|
||||
};
|
||||
+16
-3
@@ -29,6 +29,7 @@ import art.arcane.iris.generation.runtime.GenerationSessionException;
|
||||
import art.arcane.iris.generation.runtime.GenerationSessionLease;
|
||||
import art.arcane.iris.generation.biome.IrisBiome;
|
||||
import art.arcane.iris.generation.terrain.IrisRegion;
|
||||
import art.arcane.iris.world.history.SavedBiomeUnavailableException;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState;
|
||||
import art.arcane.iris.modded.ModdedEngineBootstrap;
|
||||
import art.arcane.iris.modded.ModdedScheduler;
|
||||
@@ -66,12 +67,22 @@ public final class ModdedDustRevealer {
|
||||
IrisLanguage.plain(RuntimeUiMessages.DUST_IRIS_WORLD_REQUIRED)));
|
||||
return;
|
||||
}
|
||||
describe(player, level, engine, pos);
|
||||
|
||||
int relativeY = pos.y() - engine.getMinHeight();
|
||||
String key = safe(
|
||||
String key;
|
||||
try {
|
||||
describe(player, level, engine, pos);
|
||||
key = safe(
|
||||
"object lookup at " + coordinates(pos),
|
||||
() -> engine.getObjectPlacementKey(pos.x(), relativeY, pos.z()));
|
||||
} catch (SavedBiomeUnavailableException unavailable) {
|
||||
if (!unavailable.isLoading()) {
|
||||
ModdedIrisLog.error("Iris dust saved biome lookup failed at {}", coordinates(pos), unavailable);
|
||||
}
|
||||
player.sendSystemMessage(NativeCommandText.literal(IrisLanguage.plain(unavailable.isLoading()
|
||||
? RuntimeUiMessages.DUST_BIOME_LOADING
|
||||
: RuntimeUiMessages.DUST_REVEAL_FAILED)));
|
||||
return;
|
||||
}
|
||||
if (key == null) {
|
||||
return;
|
||||
}
|
||||
@@ -435,6 +446,8 @@ public final class ModdedDustRevealer {
|
||||
private static <T> T safe(String operation, Supplier<T> supplier) {
|
||||
try {
|
||||
return supplier.get();
|
||||
} catch (SavedBiomeUnavailableException unavailable) {
|
||||
throw unavailable;
|
||||
} catch (Throwable error) {
|
||||
ModdedIrisLog.error("Iris dust {} failed", operation, error);
|
||||
return null;
|
||||
|
||||
+19
@@ -84,6 +84,25 @@ public class ModdedBlockResolutionContractTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void netherGroundCoverAcceptsNyliumAndSoulSoilButRejectsBarrenBlocks() {
|
||||
for (Block plant : new Block[]{Blocks.CRIMSON_ROOTS, Blocks.WARPED_ROOTS, Blocks.NETHER_SPROUTS}) {
|
||||
for (Block substrate : new Block[]{Blocks.CRIMSON_NYLIUM, Blocks.WARPED_NYLIUM,
|
||||
Blocks.SOUL_SOIL, Blocks.GRASS_BLOCK, Blocks.COARSE_DIRT, Blocks.PODZOL}) {
|
||||
assertTrue(plant + " on " + substrate, NativeBlockProperties.canPlaceOnto(plant, substrate));
|
||||
}
|
||||
for (Block substrate : new Block[]{Blocks.NETHERRACK, Blocks.SOUL_SAND,
|
||||
Blocks.STONE, Blocks.GRAVEL, Blocks.AIR, Blocks.WATER}) {
|
||||
assertFalse(plant + " on " + substrate, NativeBlockProperties.canPlaceOnto(plant, substrate));
|
||||
}
|
||||
}
|
||||
for (Block fungus : new Block[]{Blocks.CRIMSON_FUNGUS, Blocks.WARPED_FUNGUS}) {
|
||||
for (Block substrate : new Block[]{Blocks.CRIMSON_NYLIUM, Blocks.WARPED_NYLIUM, Blocks.SOUL_SOIL}) {
|
||||
assertTrue(fungus + " on " + substrate, NativeBlockProperties.canPlaceOnto(fungus, substrate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sulfurAndDripstoneTipsReceivePostLoadUpdates() {
|
||||
for (Block block : new Block[]{Blocks.SULFUR_SPIKE, Blocks.POINTED_DRIPSTONE}) {
|
||||
|
||||
+98
@@ -1,7 +1,23 @@
|
||||
package art.arcane.iris.modded.command;
|
||||
|
||||
import art.arcane.iris.generation.biome.IrisBiome;
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.localization.IrisLanguage;
|
||||
import art.arcane.iris.localization.RuntimeUiMessages;
|
||||
import art.arcane.iris.modded.ModdedIrisLog;
|
||||
import art.arcane.iris.world.history.SavedBiomeUnavailableException;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditPlayer;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditWorld;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeWorld;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -9,8 +25,90 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
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 ModdedDustRevealerTest {
|
||||
@BeforeClass
|
||||
public static void initializeRuntimeTypes() {
|
||||
SharedConstants.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadingBiomesShowOneNoticeAndNextInteractionShowsResolvedDetails() {
|
||||
Engine engine = mock(Engine.class);
|
||||
NativeEditPlayer player = mock(NativeEditPlayer.class);
|
||||
NativeEditWorld level = mock(NativeEditWorld.class);
|
||||
NativeWorld world = mock(NativeWorld.class);
|
||||
NativeBlockState block = mock(NativeBlockState.class);
|
||||
NativeBlockPoint position = new NativeBlockPoint(4, 80, 7);
|
||||
IrisBiome biome = new IrisBiome();
|
||||
biome.setLoadKey("saved-forest");
|
||||
when(level.world()).thenReturn(world);
|
||||
when(level.block(4, 80, 7)).thenReturn(block);
|
||||
when(block.key()).thenReturn("minecraft:stone");
|
||||
when(level.biome(position)).thenReturn(new NativeEditWorld.BiomeIdentity("minecraft:forest", 4));
|
||||
when(engine.getSurfaceBiome(4, 7))
|
||||
.thenThrow(new SavedBiomeUnavailableException("Loading", true))
|
||||
.thenReturn(biome);
|
||||
|
||||
try (MockedStatic<IrisModdedCommands> commands = mockStatic(IrisModdedCommands.class);
|
||||
MockedStatic<ModdedIrisLog> logging = mockStatic(ModdedIrisLog.class)) {
|
||||
commands.when(() -> IrisModdedCommands.engineFor(world)).thenReturn(engine);
|
||||
|
||||
ModdedDustRevealer.reveal(player, level, position);
|
||||
|
||||
ArgumentCaptor<NativeCommandText> notice = ArgumentCaptor.forClass(NativeCommandText.class);
|
||||
verify(player).sendSystemMessage(notice.capture());
|
||||
assertEquals(IrisLanguage.plain(RuntimeUiMessages.DUST_BIOME_LOADING), notice.getValue().getString());
|
||||
verify(engine, never()).getBiome(4, 80, 7);
|
||||
verify(level, never()).sound(any(), any(), any());
|
||||
logging.verifyNoInteractions();
|
||||
|
||||
clearInvocations(player);
|
||||
ModdedDustRevealer.reveal(player, level, position);
|
||||
|
||||
verify(player).sendSystemMessage(argThat(
|
||||
message -> message.getString().startsWith("Surface biome: saved-forest")));
|
||||
verify(player, never()).sendSystemMessage(argThat(
|
||||
message -> message.getString().equals(IrisLanguage.plain(RuntimeUiMessages.DUST_BIOME_LOADING))));
|
||||
logging.verifyNoInteractions();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unavailableSavedBiomesRemainLoggedAndAbortReveal() {
|
||||
Engine engine = mock(Engine.class);
|
||||
NativeEditPlayer player = mock(NativeEditPlayer.class);
|
||||
NativeEditWorld level = mock(NativeEditWorld.class);
|
||||
NativeWorld world = mock(NativeWorld.class);
|
||||
SavedBiomeUnavailableException failure = new SavedBiomeUnavailableException("Missing saved pack", false);
|
||||
when(level.world()).thenReturn(world);
|
||||
when(engine.getSurfaceBiome(4, 7)).thenThrow(failure);
|
||||
|
||||
try (MockedStatic<IrisModdedCommands> commands = mockStatic(IrisModdedCommands.class);
|
||||
MockedStatic<ModdedIrisLog> logging = mockStatic(ModdedIrisLog.class)) {
|
||||
commands.when(() -> IrisModdedCommands.engineFor(world)).thenReturn(engine);
|
||||
|
||||
ModdedDustRevealer.reveal(player, level, new NativeBlockPoint(4, 80, 7));
|
||||
|
||||
ArgumentCaptor<NativeCommandText> notice = ArgumentCaptor.forClass(NativeCommandText.class);
|
||||
verify(player).sendSystemMessage(notice.capture());
|
||||
assertEquals(IrisLanguage.plain(RuntimeUiMessages.DUST_REVEAL_FAILED), notice.getValue().getString());
|
||||
logging.verify(() -> ModdedIrisLog.error("Iris dust saved biome lookup failed at {}", "4, 80, 7", failure));
|
||||
logging.verifyNoMoreInteractions();
|
||||
verify(engine, never()).getBiome(4, 80, 7);
|
||||
verify(level, never()).sound(any(), any(), any());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void placementClassificationCoversObjectDecorationAndTerrain() {
|
||||
assertTrue(ModdedDustRevealer.placementLine(3, 10, 13, "tree")
|
||||
|
||||
+28
-9
@@ -3,6 +3,7 @@ import art.arcane.volmit.packaging.NativeDevelopmentTargets
|
||||
import io.papermc.paperweight.userdev.PaperweightUser
|
||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
||||
import io.papermc.paperweight.userdev.PaperweightUserExtension
|
||||
import org.gradle.api.artifacts.component.ComponentIdentifier
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.jvm.toolchain.JavaToolchainService
|
||||
import org.gradle.api.file.CopySpec
|
||||
@@ -178,30 +179,48 @@ nmsBindings.each { String key ->
|
||||
|
||||
def included = configurations.create('included')
|
||||
def nativeRuntime = configurations.create('nativeRuntime')
|
||||
def publishedNativeRuntime = configurations.create('publishedNativeRuntime') {
|
||||
canBeConsumed = false
|
||||
transitive = false
|
||||
resolutionStrategy.useGlobalDependencySubstitutionRules = false
|
||||
}
|
||||
repositories.configureEach { repository ->
|
||||
if (repository.name != 'publishedNativeProviders') {
|
||||
repository.content { notForConfigurations('publishedNativeRuntime') }
|
||||
}
|
||||
}
|
||||
repositories.maven {
|
||||
name = 'publishedNativeProviders'
|
||||
url = uri('https://jitpack.io/')
|
||||
content {
|
||||
onlyForConfigurations('publishedNativeRuntime')
|
||||
includeGroup('com.github.VolmitSoftware.VolmLib')
|
||||
}
|
||||
}
|
||||
def nativeRuntimeOutput = layout.buildDirectory.dir('generated/native-runtime')
|
||||
def nativeRuntimeRules = layout.buildDirectory.file('generated/native-runtime-keep.pro')
|
||||
List<String> nativeRuntimeModules = ['native-common'] + nmsBindings.collect { String adapter -> 'native-' + adapter }
|
||||
dependencies {
|
||||
(['native-common'] + nmsBindings.collect { String adapter -> 'native-' + adapter }).each { String module ->
|
||||
nativeRuntimeModules.each { String module ->
|
||||
add('nativeRuntime', "com.github.VolmitSoftware.VolmLib:${module}:${volmLibCoordinate.tokenize(':').last()}") {
|
||||
transitive = false
|
||||
}
|
||||
add('publishedNativeRuntime', "com.github.VolmitSoftware.VolmLib:${module}:${volmLibCoordinate.tokenize(':').last()}")
|
||||
}
|
||||
}
|
||||
def generateNativeRuntime = tasks.register('generateNativeRuntime') {
|
||||
inputs.files(nativeRuntime)
|
||||
inputs.files(publishedNativeRuntime)
|
||||
inputs.property('version', volmLibCoordinate.tokenize(':').last())
|
||||
inputs.property('adapters', nmsBindings)
|
||||
outputs.dir(nativeRuntimeOutput)
|
||||
outputs.file(nativeRuntimeRules)
|
||||
doLast {
|
||||
Map<String, File> artifacts = new TreeMap<>()
|
||||
nativeRuntime.resolvedConfiguration.resolvedArtifacts.each { artifact ->
|
||||
String module = artifact.moduleVersion.id.name
|
||||
if (module.startsWith('v') && module.contains('_R')) {
|
||||
module = 'native-' + module
|
||||
}
|
||||
artifacts.put(module, artifact.file)
|
||||
Map<ComponentIdentifier, File> resolved = new LinkedHashMap<>()
|
||||
publishedNativeRuntime.incoming.artifacts.artifacts.each { artifact ->
|
||||
resolved.put(artifact.id.componentIdentifier, artifact.file)
|
||||
}
|
||||
Map<String, File> artifacts = NativeRuntimeArtifacts.publishedArtifacts(resolved,
|
||||
nativeRuntimeModules, volmLibCoordinate.tokenize(':').last())
|
||||
NativeRuntimeArtifacts.generate(artifacts, volmLibCoordinate.tokenize(':').last(),
|
||||
nativeRuntimeOutput.get().asFile, nativeRuntimeRules.get().asFile)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.artifacts.component.ComponentIdentifier;
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -15,6 +17,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
@@ -27,6 +30,27 @@ public final class NativeRuntimeArtifacts {
|
||||
private NativeRuntimeArtifacts() {
|
||||
}
|
||||
|
||||
public static Map<String, File> publishedArtifacts(Map<ComponentIdentifier, File> resolved,
|
||||
List<String> modules, String version) {
|
||||
Map<String, File> artifacts = new TreeMap<>();
|
||||
for (Map.Entry<ComponentIdentifier, File> entry : resolved.entrySet()) {
|
||||
if (!(entry.getKey() instanceof ModuleComponentIdentifier component)
|
||||
|| !GROUP.equals(component.getGroup()) || !version.equals(component.getVersion())
|
||||
|| !modules.contains(component.getModule())) {
|
||||
throw new GradleException("Native runtime providers must resolve to published coordinates at "
|
||||
+ version + ": " + entry.getKey().getDisplayName());
|
||||
}
|
||||
if (artifacts.put(component.getModule(), entry.getValue()) != null) {
|
||||
throw new GradleException("Duplicate native runtime provider: " + component.getModule());
|
||||
}
|
||||
}
|
||||
if (!artifacts.keySet().equals(new TreeSet<>(modules))) {
|
||||
throw new GradleException("Published native runtime providers differ: " + artifacts.keySet()
|
||||
+ "; expected " + modules);
|
||||
}
|
||||
return artifacts;
|
||||
}
|
||||
|
||||
public static void generate(Map<String, File> artifacts, String version, File output, File rules) throws IOException {
|
||||
List<String> manifest = new ArrayList<>();
|
||||
manifest.add("modules=" + String.join(",", artifacts.keySet()));
|
||||
|
||||
@@ -1,20 +1,66 @@
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.artifacts.ModuleIdentifier;
|
||||
import org.gradle.api.artifacts.component.ComponentIdentifier;
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
|
||||
import org.junit.Test;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class NativeRuntimeArtifactsTest {
|
||||
@Test
|
||||
public void acceptsOnlyRequestedPublishedCoordinates() {
|
||||
File common = new File("common.jar");
|
||||
File provider = new File("provider.jar");
|
||||
Map<ComponentIdentifier, File> resolved = Map.of(
|
||||
module("native-common", "revision"), common,
|
||||
module("native-v26_2_R1", "revision"), provider);
|
||||
assertEquals(Map.of("native-common", common, "native-v26_2_R1", provider),
|
||||
NativeRuntimeArtifacts.publishedArtifacts(resolved,
|
||||
List.of("native-common", "native-v26_2_R1"), "revision"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectsCompositeProjectArtifacts() {
|
||||
ComponentIdentifier project = () -> "project :VolmLib:native-common";
|
||||
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.publishedArtifacts(
|
||||
Map.of(project, new File("native-common-local-SNAPSHOT.jar")), List.of("native-common"), "revision"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectsUnexpectedCoordinates() {
|
||||
List<ComponentIdentifier> invalid = List.of(
|
||||
new PublishedModule("other.group", "native-common", "revision"),
|
||||
module("native-common", "other-revision"),
|
||||
module("native-v26_3_R1", "revision"));
|
||||
for (ComponentIdentifier component : invalid) {
|
||||
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.publishedArtifacts(
|
||||
Map.of(component, new File("provider.jar")), List.of("native-common"), "revision"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectsMissingProviders() {
|
||||
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.publishedArtifacts(
|
||||
Map.of(module("native-common", "revision"), new File("common.jar")),
|
||||
List.of("native-common", "native-v26_2_R1"), "revision"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recordsExactDependenciesAndRetainsSharedProviderReferences() throws Exception {
|
||||
Path directory = Files.createTempDirectory("native-artifacts");
|
||||
@@ -31,6 +77,14 @@ public class NativeRuntimeArtifactsTest {
|
||||
File rules = directory.resolve("keep.pro").toFile();
|
||||
NativeRuntimeArtifacts.generate(Map.of("native-common", provider, "native-v26_2_R1", provider),
|
||||
"revision", directory.toFile(), rules);
|
||||
Properties manifest = new Properties();
|
||||
try (Reader reader = Files.newBufferedReader(directory.resolve(NativeRuntimeArtifacts.MANIFEST))) {
|
||||
manifest.load(reader);
|
||||
}
|
||||
String digest = HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256")
|
||||
.digest(Files.readAllBytes(provider.toPath())));
|
||||
assertEquals(digest, manifest.getProperty("native-common.sha256"));
|
||||
assertEquals(digest, manifest.getProperty("native-v26_2_R1.sha256"));
|
||||
assertTrue(Files.readString(rules.toPath()).contains("-keep class art.arcane.volmlib.util.collection.KList { *; }"));
|
||||
File plugin = directory.resolve("plugin.jar").toFile();
|
||||
try (JarOutputStream jar = new JarOutputStream(Files.newOutputStream(plugin.toPath()))) {
|
||||
@@ -42,4 +96,41 @@ public class NativeRuntimeArtifactsTest {
|
||||
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.verify(plugin, List.of("v26_3_R1"), "revision"));
|
||||
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.verify(plugin, List.of("v26_2_R1"), "other"));
|
||||
}
|
||||
|
||||
private static ModuleComponentIdentifier module(String module, String version) {
|
||||
return new PublishedModule("com.github.VolmitSoftware.VolmLib", module, version);
|
||||
}
|
||||
|
||||
private record PublishedModule(String group, String module, String version)
|
||||
implements ModuleComponentIdentifier, ModuleIdentifier {
|
||||
@Override
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModule() {
|
||||
return module;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleIdentifier getModuleIdentifier() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return module;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return group + ":" + module + ":" + version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
art/arcane/iris/diagnostics/LogFilterSVC.java
|
||||
art/arcane/iris/structure/BulkStructureImporter.java
|
||||
art/arcane/iris/structure/VillageImporter.java
|
||||
art/arcane/iris/world/lifecycle/CapabilitySnapshot.java
|
||||
art/arcane/iris/world/lifecycle/WorldLifecycleSupport.java
|
||||
art/arcane/iris/world/runtime/WorldRuntimeControlService.java
|
||||
|
||||
@@ -66,6 +66,7 @@ art/arcane/iris/pack/validation/IrisCompatabilityBlockFilter.java
|
||||
art/arcane/iris/pack/validation/IrisCompatabilityItemFilter.java
|
||||
art/arcane/iris/pack/value/IrisDirection.java
|
||||
art/arcane/iris/platform/bootstrap/Bindings.java
|
||||
art/arcane/iris/platform/bootstrap/SlimJar.java
|
||||
art/arcane/iris/platform/bukkit/BoardSVC.java
|
||||
art/arcane/iris/platform/bukkit/BukkitBiome.java
|
||||
art/arcane/iris/platform/bukkit/BukkitBlockResolution.java
|
||||
@@ -80,6 +81,12 @@ art/arcane/iris/platform/bukkit/BukkitWorld.java
|
||||
art/arcane/iris/platform/bukkit/BukkitWorldBinding.java
|
||||
art/arcane/iris/platform/bukkit/chunk/ChunkTickets.java
|
||||
art/arcane/iris/platform/bukkit/chunk/TicketHolder.java
|
||||
art/arcane/iris/platform/bukkit/nms/BukkitBinding.java
|
||||
art/arcane/iris/platform/bukkit/nms/BukkitBiomePolicy.java
|
||||
art/arcane/iris/platform/bukkit/nms/BukkitGenerationPolicy.java
|
||||
art/arcane/iris/platform/bukkit/nms/BukkitGeneratorContext.java
|
||||
art/arcane/iris/platform/bukkit/nms/BukkitTileWriteScheduler.java
|
||||
art/arcane/iris/platform/bukkit/nms/BukkitWorldLifecyclePolicy.java
|
||||
art/arcane/iris/platform/bukkit/nms/INMS.java
|
||||
art/arcane/iris/platform/bukkit/nms/INMSBinding.java
|
||||
art/arcane/iris/platform/bukkit/nms/MinecraftVersion.java
|
||||
@@ -88,17 +95,17 @@ art/arcane/iris/platform/bukkit/plugin/Chunks.java
|
||||
art/arcane/iris/platform/bukkit/plugin/IrisService.java
|
||||
art/arcane/iris/platform/bukkit/plugin/VolmitPlugin.java
|
||||
art/arcane/iris/platform/bukkit/plugin/VolmitSender.java
|
||||
art/arcane/iris/platform/bukkit/registry/Attributes.java
|
||||
art/arcane/iris/platform/bukkit/registry/KeyedRegistry.java
|
||||
art/arcane/iris/platform/bukkit/registry/Materials.java
|
||||
art/arcane/iris/platform/bukkit/registry/Particles.java
|
||||
art/arcane/iris/platform/bukkit/registry/RegistryTypeAdapter.java
|
||||
art/arcane/iris/platform/bukkit/registry/RegistryUtil.java
|
||||
art/arcane/iris/platform/generation/BukkitChunkGenerator.java
|
||||
art/arcane/iris/platform/generation/DummyBiomeProvider.java
|
||||
art/arcane/iris/platform/generation/DummyChunkGenerator.java
|
||||
art/arcane/iris/platform/generation/EngineBukkitOps.java
|
||||
art/arcane/iris/platform/reflect/KeyedType.java
|
||||
art/arcane/iris/platform/registry/Attributes.java
|
||||
art/arcane/iris/platform/registry/KeyedRegistry.java
|
||||
art/arcane/iris/platform/registry/Materials.java
|
||||
art/arcane/iris/platform/registry/Particles.java
|
||||
art/arcane/iris/platform/registry/RegistryTypeAdapter.java
|
||||
art/arcane/iris/platform/registry/RegistryUtil.java
|
||||
art/arcane/iris/structure/BulkStructureImporter.java
|
||||
art/arcane/iris/structure/FeatureImporter.java
|
||||
art/arcane/iris/structure/StructureCaptureImporter.java
|
||||
|
||||
@@ -323,6 +323,9 @@ final class DecoratorCore {
|
||||
int xf, int zf, int realX, int realZ,
|
||||
int height, int max, Hunk<NativeBlockState> data,
|
||||
RNG rng, IrisData irisData, EngineMantle mantle) {
|
||||
if (!hasFloatingSurface(data, xf, height, zf)) {
|
||||
return;
|
||||
}
|
||||
NativeBlockState bd = decorator.pickBlockData(rng, irisData, realX, realZ);
|
||||
if (bd == null) {
|
||||
return;
|
||||
@@ -367,6 +370,9 @@ final class DecoratorCore {
|
||||
int xf, int zf, int realX, int realZ,
|
||||
int height, int max, Hunk<NativeBlockState> data,
|
||||
RNG rng, IrisData irisData, EngineMantle mantle) {
|
||||
if (!hasFloatingSurface(data, xf, height, zf)) {
|
||||
return 0;
|
||||
}
|
||||
int stack = decorator.getHeight(rng, realX, realZ, irisData);
|
||||
if (decorator.isScaleStack()) {
|
||||
stack = Math.min((int) Math.ceil((double) max * ((double) stack / 100)), decorator.getAbsoluteMaxStack());
|
||||
@@ -413,6 +419,14 @@ final class DecoratorCore {
|
||||
return placed;
|
||||
}
|
||||
|
||||
private static boolean hasFloatingSurface(Hunk<NativeBlockState> data, int x, int height, int z) {
|
||||
if (height < 0 || height >= data.getHeight()) {
|
||||
return false;
|
||||
}
|
||||
NativeBlockState surface = data.get(x, height, z);
|
||||
return B.isSolid(surface);
|
||||
}
|
||||
|
||||
static NativeBlockState fixFacesForHunk(NativeBlockState b, Hunk<NativeBlockState> hunk, int rX, int rZ,
|
||||
int x, int y, int z, EngineMantle mantle) {
|
||||
if (!B.isVineBlock(b)) {
|
||||
|
||||
@@ -26,6 +26,8 @@ public enum IrisTreeProfile {
|
||||
OAK,
|
||||
@Description("Slim upright broadleaf crown.")
|
||||
BIRCH,
|
||||
@Description("Upright broadleaf crown with a rounded upper canopy and a tapered top.")
|
||||
POPLAR,
|
||||
@Description("Narrow conifer cone.")
|
||||
SPRUCE,
|
||||
@Description("Tall thin crown concentrated near the very top.")
|
||||
|
||||
+2
-2
@@ -89,7 +89,7 @@ public final class TreeDecoratorApplier {
|
||||
|
||||
private static void trunkSurface(TreeBlockCanvas canvas, IrisTreeDecorator dec, int idx, RNG rng) {
|
||||
int[][] sides = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
|
||||
String[] facings = {"west", "east", "north", "south"};
|
||||
String[] facings = {"east", "west", "south", "north"};
|
||||
for (TreeBlockCanvas.Vec v : new KList<>(canvas.getTrunk())) {
|
||||
for (int i = 0; i < sides.length; i++) {
|
||||
int nx = v.x() + sides[i][0];
|
||||
@@ -104,7 +104,7 @@ public final class TreeDecoratorApplier {
|
||||
|
||||
private static void leafSurface(TreeBlockCanvas canvas, IrisTreeDecorator dec, int idx, RNG rng) {
|
||||
int[][] sides = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
|
||||
String[] facings = {"west", "east", "north", "south"};
|
||||
String[] facings = {"east", "west", "south", "north"};
|
||||
for (TreeBlockCanvas.Vec v : new KList<>(canvas.getLeaf())) {
|
||||
for (int i = 0; i < sides.length; i++) {
|
||||
int nx = v.x() + sides[i][0];
|
||||
|
||||
@@ -27,6 +27,7 @@ public final class TreeProfiles {
|
||||
return switch (profile) {
|
||||
case OAK, DARK_OAK, CHERRY -> 1.0;
|
||||
case BIRCH -> 0.7;
|
||||
case POPLAR -> 0.8;
|
||||
case SPRUCE -> 0.55;
|
||||
case JUNGLE -> 0.45;
|
||||
case ACACIA -> 0.6;
|
||||
@@ -44,6 +45,7 @@ public final class TreeProfiles {
|
||||
return switch (profile) {
|
||||
case OAK -> new double[][]{{0.55, 0.6}, {0.65, 0.9}, {0.75, 1.0}, {0.85, 0.9}, {0.92, 0.65}, {0.98, 0.35}};
|
||||
case BIRCH -> new double[][]{{0.6, 0.45}, {0.7, 0.75}, {0.8, 0.85}, {0.88, 0.75}, {0.94, 0.45}, {0.99, 0.2}};
|
||||
case POPLAR -> new double[][]{{0.5, 0.35}, {0.62, 0.7}, {0.74, 1.0}, {0.84, 0.95}, {0.93, 0.65}, {1.0, 0.3}};
|
||||
case SPRUCE, MEGA_SPRUCE -> new double[][]{{0.3, 1.0}, {0.42, 0.85}, {0.54, 0.7}, {0.65, 0.55}, {0.75, 0.4}, {0.84, 0.25}, {0.91, 0.15}, {0.97, 0.05}};
|
||||
case JUNGLE -> new double[][]{{0.82, 0.4}, {0.88, 0.8}, {0.93, 1.0}, {0.97, 0.7}, {1.0, 0.3}};
|
||||
case ACACIA -> new double[][]{{0.9, 0.5}, {0.95, 0.8}, {0.99, 0.4}};
|
||||
|
||||
@@ -224,7 +224,7 @@ public class IrisPostModifier extends EngineAssignedModifier<NativeBlockState> {
|
||||
}
|
||||
|
||||
// Slab
|
||||
if (slabs && !river) {
|
||||
if (slabs && !river && isSolid(x, h, z, originX, originZ, currentData)) {
|
||||
//@builder
|
||||
if ((ha == h + 1 && isSolidNonSlab(x + 1, ha, z, originX, originZ, currentData))
|
||||
|| (hb == h + 1 && isSolidNonSlab(x, hb, z + 1, originX, originZ, currentData))
|
||||
|
||||
@@ -92,6 +92,7 @@ public final class RuntimeUiMessages {
|
||||
public static final TextKey DUST_COPY_BUTTON = TextKey.of("iris.runtime.dust.copy_button", "[Click to copy these stats]");
|
||||
public static final TextKey DUST_COPY_HOVER = TextKey.of("iris.runtime.dust.copy_hover", "Copy block stats to clipboard");
|
||||
public static final TextKey DUST_REVEAL_FAILED = TextKey.of("iris.runtime.dust.reveal_failed", "Object reveal failed; see the console for details.");
|
||||
public static final TextKey DUST_BIOME_LOADING = TextKey.of("iris.runtime.dust.biome_loading", "Saved biome information is loading. Try revealing this block again shortly.");
|
||||
public static final TextKey WHAT_MATERIAL = TextKey.of("iris.runtime.what.material", "Material: {material}");
|
||||
public static final TextKey WHAT_FULL_STATE = TextKey.of("iris.runtime.what.full_state", "Full: {state}");
|
||||
public static final TextKey WHAT_ITEM_COUNT = TextKey.of("iris.runtime.what.item_count", "Count: {count}");
|
||||
@@ -222,6 +223,7 @@ public final class RuntimeUiMessages {
|
||||
DUST_COPY_BUTTON,
|
||||
DUST_COPY_HOVER,
|
||||
DUST_REVEAL_FAILED,
|
||||
DUST_BIOME_LOADING,
|
||||
WHAT_MATERIAL,
|
||||
WHAT_FULL_STATE,
|
||||
WHAT_ITEM_COUNT,
|
||||
|
||||
@@ -36,6 +36,7 @@ import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.volmlib.util.localization.MessageArgument;
|
||||
import art.arcane.iris.platform.bukkit.plugin.VolmitSender;
|
||||
import art.arcane.iris.world.task.J;
|
||||
import art.arcane.iris.world.history.SavedBiomeUnavailableException;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.World;
|
||||
@@ -135,7 +136,16 @@ public class DustRevealer {
|
||||
Engine access = generator.getEngine();
|
||||
|
||||
if (access != null) {
|
||||
try {
|
||||
describe(access, world, block, sender);
|
||||
} catch (SavedBiomeUnavailableException unavailable) {
|
||||
if (!unavailable.isLoading()) {
|
||||
IrisLogging.reportError(unavailable);
|
||||
}
|
||||
sender.sendMessage(IrisLanguage.text(unavailable.isLoading()
|
||||
? RuntimeUiMessages.DUST_BIOME_LOADING : RuntimeUiMessages.DUST_REVEAL_FAILED));
|
||||
return;
|
||||
}
|
||||
|
||||
String a = access.getObjectPlacementKey(block.getX(), block.getY() - block.getWorld().getMinHeight(), block.getZ());
|
||||
if (a != null) {
|
||||
@@ -339,6 +349,8 @@ public class DustRevealer {
|
||||
private static <T> T safe(Supplier<T> supplier) {
|
||||
try {
|
||||
return supplier.get();
|
||||
} catch (SavedBiomeUnavailableException unavailable) {
|
||||
throw unavailable;
|
||||
} catch (Throwable e) {
|
||||
IrisLogging.reportError(e);
|
||||
return null;
|
||||
|
||||
@@ -1378,6 +1378,7 @@ totals = "Gesamt: +{wood} Holz ({branches} Äste), {converted} Blätter->Holz, ~
|
||||
position_set = "Position {position} auf {x}, {y}, {z} gesetzt"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "Die gespeicherten Biominformationen werden geladen. Versuche in Kürze erneut, diesen Block zu untersuchen."
|
||||
iris_world_required = "Diese Dimension wurde nicht von Iris generiert."
|
||||
found_object = "Objekt {object} gefunden"
|
||||
revealed = "{count} Block/Blöcke von {object} aufgedeckt"
|
||||
|
||||
@@ -1378,6 +1378,7 @@ totals = "Totales: +{wood} de madera ({branches} ramas), {converted} hojas->made
|
||||
position_set = "Posición {position} fijada en {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "Se está cargando la información guardada del bioma. Vuelve a intentar revelar este bloque dentro de unos instantes."
|
||||
iris_world_required = "Esta dimensión no fue generada por Iris."
|
||||
found_object = "Objeto encontrado: {object}"
|
||||
revealed = "Se revelaron {count} bloque(s) de {object}"
|
||||
|
||||
@@ -1383,6 +1383,7 @@ totals = "Yhteensä: +{wood} puu ({branches} konttorit), {converted} Lehti->puu{
|
||||
position_set = "Sijainti {position} asetettu {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "Tallennettuja biomitietoja ladataan. Yritä paljastaa tämän lohkon tiedot uudelleen hetken kuluttua."
|
||||
iris_world_required = "Iris ei ole luonut tätä ulottuvuutta."
|
||||
found_object = "Löytyi {object}"
|
||||
revealed = "{object}:n paljastamat {count}-katkokset"
|
||||
|
||||
@@ -1378,6 +1378,7 @@ totals = "Totaux : +{wood} bois ({branches} branches), {converted} feuilles->boi
|
||||
position_set = "Position {position} définie sur {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "Les informations enregistrées du biome sont en cours de chargement. Réessaie de révéler ce bloc dans quelques instants."
|
||||
iris_world_required = "Cette dimension n'a pas été générée par Iris."
|
||||
found_object = "Objet trouvé : {object}"
|
||||
revealed = "{count} bloc(s) de {object} révélés"
|
||||
|
||||
@@ -1383,6 +1383,7 @@ totals = "סיכומים:+{wood} עץ ({branches} סניפים) {converted} הע
|
||||
position_set = "מיקום {position} להגדיר {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "נתוני הביומה השמורים נטענים. נסו לחשוף את הבלוק הזה שוב בעוד רגע."
|
||||
iris_world_required = "ממד זה לא נוצר על ידי Iris."
|
||||
found_object = "אובייקט {object}"
|
||||
revealed = "נחשף {count} בלוקים (s) {object}"
|
||||
|
||||
@@ -1378,6 +1378,7 @@ totals = "Totale: +{wood} legno ({branches} rami), {converted} foglie->legno, ~{
|
||||
position_set = "Posizione {position} impostata a {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "Caricamento delle informazioni salvate sul bioma in corso. Riprova a rivelare questo blocco tra qualche istante."
|
||||
iris_world_required = "Questa dimensione non è stata generata da Iris."
|
||||
found_object = "Oggetto trovato {object}"
|
||||
revealed = "Rivelato {count} blocco(i) di {object}"
|
||||
|
||||
@@ -1381,6 +1381,7 @@ totals = "合計: 木材 +{wood}(枝 {branches})、葉 -> 木材 {converted}
|
||||
position_set = "選択位置 {position} を {x}, {y}, {z} に設定しました"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "保存されたバイオーム情報を読み込んでいます。少し待ってから、このブロックの情報を再度確認してください。"
|
||||
iris_world_required = "このディメンションは Iris によって生成されていません。"
|
||||
found_object = "オブジェクト {object} が見つかりました"
|
||||
revealed = "{object} のブロックを {count} 個表示しました"
|
||||
|
||||
@@ -1381,6 +1381,7 @@ totals = "합계: 나무 +{wood}개(가지 {branches}개), 잎->나무 {converte
|
||||
position_set = "선택 위치 {position}을(를) {x}, {y}, {z}(으)로 설정했습니다"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "저장된 바이옴 정보를 불러오는 중입니다. 잠시 후 이 블록의 정보를 다시 확인해 주세요."
|
||||
iris_world_required = "이 차원은 Iris가 생성하지 않았습니다."
|
||||
found_object = "오브젝트 {object} 발견"
|
||||
revealed = "{object}의 블록 {count}개를 드러냈습니다"
|
||||
|
||||
@@ -1383,6 +1383,7 @@ totals = "Iš viso: +{wood} medienos ({branches} filialai), {converted} Lapai->m
|
||||
position_set = "Padėtis {position} nustatyti {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "Įkeliama išsaugota biomo informacija. Netrukus pabandyk dar kartą atskleisti šio bloko informaciją."
|
||||
iris_world_required = "Šio matmens nesugeneravo Iris."
|
||||
found_object = "Rastas objektas {object}"
|
||||
revealed = "Atšaldytas {count} blokas (-iai) {object}"
|
||||
|
||||
@@ -1378,6 +1378,7 @@ totals = "Totaal: +{wood} hout ({branches} takken), {converted} Bladeren->hout,
|
||||
position_set = "Positie {position} ingesteld op {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "De opgeslagen bioominformatie wordt geladen. Probeer dit blok over enkele ogenblikken opnieuw te onthullen."
|
||||
iris_world_required = "Deze dimensie is niet door Iris gegenereerd."
|
||||
found_object = "Object gevonden {object}"
|
||||
revealed = "Onthuld {count} blok(s) van {object}"
|
||||
|
||||
@@ -1383,6 +1383,7 @@ totals = "Suma: +{wood} drewno ({branches} ), {converted} liści->drewno, ~{dist
|
||||
position_set = "Pozycja {position} ustawiony na {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "Trwa wczytywanie zapisanych informacji o biomie. Za chwilę spróbuj ponownie ujawnić informacje o tym bloku."
|
||||
iris_world_required = "Ten wymiar nie został wygenerowany przez Iris."
|
||||
found_object = "Znaleziono obiekt {object}"
|
||||
revealed = "Ujawnione {count} blok (-y) {object}"
|
||||
|
||||
@@ -1378,6 +1378,7 @@ totals = "Total: +{wood} Madeira ({branches} ramos), {converted} folhas->madeira
|
||||
position_set = "Posição {position} definir para {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "As informações guardadas do bioma estão a ser carregadas. Tenta revelar este bloco novamente dentro de instantes."
|
||||
iris_world_required = "Esta dimensão não foi gerada pelo Iris."
|
||||
found_object = "Objeto encontrado {object}"
|
||||
revealed = "Revogado {count} bloco( s) de {object}"
|
||||
|
||||
@@ -1383,6 +1383,7 @@ totals = "Итоги: +{wood}древесина{branches}ветви,{converted}
|
||||
position_set = "Позиция {position} устанавливать {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "Сохранённые сведения о биоме загружаются. Попробуйте снова раскрыть информацию об этом блоке через несколько секунд."
|
||||
iris_world_required = "Это измерение не было создано Iris."
|
||||
found_object = "Найден объект {object}"
|
||||
revealed = "раскрытый {count} блок(ы) {object}"
|
||||
|
||||
@@ -1383,6 +1383,7 @@ totals = "Toplamlar: +{wood} wood ({branches} şubeler) {converted} Yapraklar->A
|
||||
position_set = "Pozisyon pozisyonu {position} set toplan {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "Kaydedilmiş biyom bilgileri yükleniyor. Bu bloğu açığa çıkarmayı kısa bir süre sonra tekrar deneyin."
|
||||
iris_world_required = "Bu boyut Iris tarafından oluşturulmadı."
|
||||
found_object = "bulundu object bulunamadı {object}"
|
||||
revealed = "Ortaya çıktı {count} blok (s) {object}"
|
||||
|
||||
@@ -1381,6 +1381,7 @@ totals = "Tổng: +{wood} gỗ ({branches} chi nhánh), {converted} để lại-
|
||||
position_set = "Vị trí {position} đặt {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "Đang tải thông tin quần xã sinh vật đã lưu. Hãy thử khám phá lại khối này sau giây lát."
|
||||
iris_world_required = "Chiều không gian này không được Iris tạo ra."
|
||||
found_object = "Đối tượng đã tìm {object}"
|
||||
revealed = "Đã tiết lộ{count}khối{object}"
|
||||
|
||||
@@ -1381,6 +1381,7 @@ totals = "共计:+{wood} 木材({branches} 分部门), {converted} 叶->木头,~
|
||||
position_set = "立场 {position} 设置为 {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "正在加载已保存的生物群系信息。请稍后再次尝试揭示此方块的信息。"
|
||||
iris_world_required = "此维度不是由 Iris 生成的。"
|
||||
found_object = "找到对象 {object}"
|
||||
revealed = "被揭露{count}区块{object}"
|
||||
|
||||
@@ -1381,6 +1381,7 @@ totals = "共計:+{wood} 木材({branches} 分部門), {converted} 葉->木頭,~
|
||||
position_set = "立場 {position} 設定為 {x}, {y}, {z}"
|
||||
|
||||
[iris.runtime.dust]
|
||||
biome_loading = "正在載入已儲存的生態域資訊。請稍後再次嘗試揭示此方塊的資訊。"
|
||||
iris_world_required = "此維度不是由 Iris 生成的。"
|
||||
found_object = "找到物件 {object}"
|
||||
revealed = "被揭露{count}區塊{object}"
|
||||
|
||||
@@ -298,6 +298,7 @@ public class DecoratorCoreTest {
|
||||
when(decorator.pickBlockDataTop(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant);
|
||||
|
||||
Hunk<NativeBlockState> output = Hunk.newArrayHunk(1, 2, 1);
|
||||
output.set(0, 0, 0, sturdyState());
|
||||
int placed = DecoratorCore.placeFloatingStacked(
|
||||
decorator, 0, 0, 0, 0, 0, 3, output, new RNG(1L), data, null);
|
||||
|
||||
@@ -305,6 +306,72 @@ public class DecoratorCoreTest {
|
||||
assertSame(decorant, output.get(0, 1, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void floatingDecoratorsRejectUnsupportedSurface() {
|
||||
IrisDecorator decorator = mock(IrisDecorator.class);
|
||||
IrisData data = mock(IrisData.class);
|
||||
NativeBlockState decorant = mock(NativeBlockState.class);
|
||||
NativeBlockState air = airState();
|
||||
NativeBlockState fluid = mock(NativeBlockState.class);
|
||||
NativeBlockState plant = mock(NativeBlockState.class);
|
||||
when(plant.key()).thenReturn("minecraft:short_grass");
|
||||
when(fluid.isFluid()).thenReturn(true);
|
||||
when(decorant.key()).thenReturn("minecraft:cobblestone_slab");
|
||||
when(decorator.getHeight(any(RNG.class), anyDouble(), anyDouble(), eq(data))).thenReturn(2);
|
||||
when(decorator.getTopThreshold()).thenReturn(1.0);
|
||||
when(decorator.pickBlockData(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant);
|
||||
when(decorator.pickBlockDataTop(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant);
|
||||
|
||||
for (NativeBlockState surface : new NativeBlockState[]{null, air, fluid, plant}) {
|
||||
Hunk<NativeBlockState> output = Hunk.newArrayHunk(1, 5, 1);
|
||||
output.set(0, 0, 0, sturdyState());
|
||||
output.set(0, 2, 0, surface);
|
||||
output.set(0, 3, 0, air);
|
||||
output.set(0, 4, 0, air);
|
||||
|
||||
DecoratorCore.placeFloatingSimple(
|
||||
decorator, 0, 0, 0, 0, 2, 3, output, new RNG(1L), data, null);
|
||||
assertSame(air, output.get(0, 3, 0));
|
||||
|
||||
int placed = DecoratorCore.placeFloatingStacked(
|
||||
decorator, 0, 0, 0, 0, 2, 3, output, new RNG(1L), data, null);
|
||||
assertEquals(0, placed);
|
||||
assertSame(surface, output.get(0, 2, 0));
|
||||
assertSame(air, output.get(0, 3, 0));
|
||||
assertSame(air, output.get(0, 4, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void floatingDecoratorsPreserveSupportedPlacementWithoutMaterialGating() {
|
||||
IrisDecorator decorator = mock(IrisDecorator.class);
|
||||
IrisData data = mock(IrisData.class);
|
||||
NativeBlockState surface = mock(NativeBlockState.class);
|
||||
NativeBlockState decorant = mock(NativeBlockState.class);
|
||||
when(surface.isSolid()).thenReturn(true);
|
||||
when(decorant.key()).thenReturn("minecraft:cobblestone_slab");
|
||||
when(decorator.getHeight(any(RNG.class), anyDouble(), anyDouble(), eq(data))).thenReturn(2);
|
||||
when(decorator.getTopThreshold()).thenReturn(1.0);
|
||||
when(decorator.pickBlockData(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant);
|
||||
when(decorator.pickBlockDataTop(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant);
|
||||
Hunk<NativeBlockState> output = Hunk.newArrayHunk(1, 4, 1);
|
||||
output.set(0, 1, 0, surface);
|
||||
output.set(0, 2, 0, airState());
|
||||
output.set(0, 3, 0, airState());
|
||||
|
||||
DecoratorCore.placeFloatingSimple(
|
||||
decorator, 0, 0, 0, 0, 1, 3, output, new RNG(1L), data, null);
|
||||
assertSame(decorant, output.get(0, 2, 0));
|
||||
output.set(0, 2, 0, airState());
|
||||
|
||||
int placed = DecoratorCore.placeFloatingStacked(
|
||||
decorator, 0, 0, 0, 0, 1, 3, output, new RNG(1L), data, null);
|
||||
assertEquals(2, placed);
|
||||
assertSame(surface, output.get(0, 1, 0));
|
||||
assertSame(decorant, output.get(0, 2, 0));
|
||||
assertSame(decorant, output.get(0, 3, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tallSurfacePlantDoesNotPlaceWhenUpperTargetIsOccupied() {
|
||||
IrisDecorator decorator = mock(IrisDecorator.class);
|
||||
@@ -337,6 +404,7 @@ public class DecoratorCoreTest {
|
||||
when(decorator.pickBlockData(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(plant);
|
||||
|
||||
Hunk<NativeBlockState> output = Hunk.newArrayHunk(1, 4, 1);
|
||||
output.set(0, 0, 0, sturdyState());
|
||||
output.set(0, 1, 0, air);
|
||||
output.set(0, 2, 0, occupied);
|
||||
|
||||
@@ -737,6 +805,7 @@ public class DecoratorCoreTest {
|
||||
private NativeBlockState sturdyState() {
|
||||
NativeBlockState support = mock(NativeBlockState.class);
|
||||
BlockData blockData = mock(BlockData.class);
|
||||
when(support.isSolid()).thenReturn(true);
|
||||
when(support.nativeHandle()).thenReturn(blockData);
|
||||
when(blockData.isFaceSturdy(any(), eq(BlockSupport.FULL))).thenReturn(true);
|
||||
return support;
|
||||
|
||||
+2
@@ -109,6 +109,8 @@ public class IrisSugarCanePlacementTest {
|
||||
local = size / 2;
|
||||
output = Hunk.newArrayHunk(size, 8, size);
|
||||
when(air.isAir()).thenReturn(true);
|
||||
when(stone.isSolid()).thenReturn(true);
|
||||
when(soil.isSolid()).thenReturn(true);
|
||||
when(water.isWater()).thenReturn(true);
|
||||
when(cane.canPlaceOnto(soil)).thenReturn(true);
|
||||
when(cane.canPlaceOnto(cane)).thenReturn(true);
|
||||
|
||||
+65
@@ -5,6 +5,7 @@ import org.junit.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -88,6 +89,70 @@ public class ProceduralTreeGeneratorTest {
|
||||
assertTrue("leaves should rise above the trunk top", maxLeafY >= height - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void poplarCrownIsBroaderAndHigherThanColumnarCrown() {
|
||||
TreeBlockCanvas poplar = profileCanopy(IrisTreeProfile.POPLAR);
|
||||
TreeBlockCanvas columnar = profileCanopy(IrisTreeProfile.COLUMNAR);
|
||||
int poplarWidth = 0;
|
||||
int columnarWidth = 0;
|
||||
int poplarBottom = Integer.MAX_VALUE;
|
||||
int columnarBottom = Integer.MAX_VALUE;
|
||||
for (TreeBlockCanvas.Vec leaf : poplar.getLeaf()) {
|
||||
poplarWidth = Math.max(poplarWidth, Math.abs(leaf.x()));
|
||||
poplarBottom = Math.min(poplarBottom, leaf.y());
|
||||
}
|
||||
for (TreeBlockCanvas.Vec leaf : columnar.getLeaf()) {
|
||||
columnarWidth = Math.max(columnarWidth, Math.abs(leaf.x()));
|
||||
columnarBottom = Math.min(columnarBottom, leaf.y());
|
||||
}
|
||||
|
||||
assertTrue(poplarWidth > columnarWidth);
|
||||
assertTrue(poplarBottom > columnarBottom);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void branchingPoplarVariantsRetainSupportedLeavesAndOrientedWood() {
|
||||
IrisProceduralTree tree = new IrisProceduralTree()
|
||||
.setProfile(IrisTreeProfile.POPLAR)
|
||||
.setTrunk("minecraft:poplar_log")
|
||||
.setLeaves("minecraft:orange_poplar_leaves")
|
||||
.setLeanAngle(7);
|
||||
IrisTreeBranches branches = new IrisTreeBranches()
|
||||
.setProbabilityFunction(IrisTreeBranchProbability.TOP_HEAVY)
|
||||
.setProbabilityExponent(2)
|
||||
.setLengthFunction(IrisTreeFunction.CONSTANT)
|
||||
.setLengthConstant(3)
|
||||
.setElevation(35)
|
||||
.setClusterRadius(2);
|
||||
tree.getCanopy().setBranches(branches);
|
||||
for (int height = 7; height <= 11; height++) {
|
||||
TreeBlockCanvas canvas = new TreeBlockCanvas();
|
||||
List<int[]> endpoints = new ArrayList<>();
|
||||
double[][] offsets = TreeTrunkBuilder.build(canvas, tree, height).getFirst().offsets();
|
||||
TreeCanopyBuilder.build(canvas, tree, height, offsets, 0, height * 7919L, endpoints);
|
||||
TreeSupport.ensureLeavesSupported(canvas, 24);
|
||||
|
||||
assertFalse(endpoints.isEmpty());
|
||||
assertFalse(canvas.getLeaf().isEmpty());
|
||||
Map<TreeBlockCanvas.Vec, Integer> distances = TreePlausibility.computeDistances(canvas.getTrunk(), canvas.getLeaf());
|
||||
for (TreeBlockCanvas.Vec leaf : canvas.getLeaf()) {
|
||||
assertNotNull(distances.get(leaf));
|
||||
assertTrue(distances.get(leaf) <= 6);
|
||||
}
|
||||
for (TreeBlockCanvas.Vec wood : canvas.getTrunk()) {
|
||||
assertTrue(canvas.get(wood.x(), wood.y(), wood.z()).axis() != TreeBlockCanvas.Axis.NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static TreeBlockCanvas profileCanopy(IrisTreeProfile profile) {
|
||||
IrisProceduralTree tree = new IrisProceduralTree().setProfile(profile);
|
||||
TreeBlockCanvas canvas = new TreeBlockCanvas();
|
||||
double[][] offsets = TreeTrunkBuilder.build(canvas, tree, 14).getFirst().offsets();
|
||||
TreeCanopyBuilder.build(canvas, tree, 14, offsets, 0, 1234L, null);
|
||||
return canvas;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hollowCanopyHasFewerLeavesThanFilled() {
|
||||
TreeBlockCanvas filled = canopyOnly(IrisTreeLeafMode.FILLED);
|
||||
|
||||
+54
@@ -6,8 +6,46 @@ import org.junit.Test;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class TreeDecoratorApplierTest {
|
||||
@Test
|
||||
public void trunkAttachmentsFaceAwayFromTheirWoodSupport() {
|
||||
TreeBlockCanvas canvas = new TreeBlockCanvas();
|
||||
canvas.setTrunk(0, 2, 0, TreeBlockCanvas.Role.TRUNK, TreeBlockCanvas.Axis.Y);
|
||||
IrisProceduralTree tree = surfaceTree(IrisTreeDecoratorTarget.TRUNK_SURFACE, true);
|
||||
|
||||
TreeDecoratorApplier.apply(canvas, tree, 918L, List.of());
|
||||
|
||||
assertOutwardFacings(canvas);
|
||||
assertEquals(1, canvas.getTrunk().size());
|
||||
assertEquals(5, canvas.getCells().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void leafAttachmentsFaceAwayFromTheirLeafSupport() {
|
||||
TreeBlockCanvas canvas = new TreeBlockCanvas();
|
||||
canvas.setLeaf(0, 2, 0, TreeBlockCanvas.Role.LEAF);
|
||||
IrisProceduralTree tree = surfaceTree(IrisTreeDecoratorTarget.LEAF_SURFACE, true);
|
||||
|
||||
TreeDecoratorApplier.apply(canvas, tree, 918L, List.of());
|
||||
|
||||
assertOutwardFacings(canvas);
|
||||
assertEquals(1, canvas.getLeaf().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trunkAttachmentsKeepExplicitBlockFacingWhenAxisAwarenessIsDisabled() {
|
||||
TreeBlockCanvas canvas = new TreeBlockCanvas();
|
||||
canvas.setTrunk(0, 2, 0, TreeBlockCanvas.Role.TRUNK, TreeBlockCanvas.Axis.Y);
|
||||
IrisProceduralTree tree = surfaceTree(IrisTreeDecoratorTarget.TRUNK_SURFACE, false);
|
||||
|
||||
TreeDecoratorApplier.apply(canvas, tree, 918L, List.of());
|
||||
|
||||
assertEquals(TreeBlockCanvas.Role.DECORATOR, canvas.get(1, 2, 0).role());
|
||||
assertNull(canvas.get(1, 2, 0).facing());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void branchTipsExtendOutwardThroughLeavesWithoutReplacingWood() {
|
||||
TreeBlockCanvas canvas = new TreeBlockCanvas();
|
||||
@@ -39,4 +77,20 @@ public class TreeDecoratorApplierTest {
|
||||
|
||||
assertEquals(11, canvas.getCells().size());
|
||||
}
|
||||
|
||||
private static IrisProceduralTree surfaceTree(IrisTreeDecoratorTarget target, boolean axisAware) {
|
||||
IrisTreeDecorator decorator = new IrisTreeDecorator()
|
||||
.setTarget(target)
|
||||
.setBlock("minecraft:shelf_mushroom")
|
||||
.setChance(1D)
|
||||
.setAxisAware(axisAware);
|
||||
return new IrisProceduralTree().setDecorators(new KList<>(decorator));
|
||||
}
|
||||
|
||||
private static void assertOutwardFacings(TreeBlockCanvas canvas) {
|
||||
assertEquals("east", canvas.get(1, 2, 0).facing());
|
||||
assertEquals("west", canvas.get(-1, 2, 0).facing());
|
||||
assertEquals("south", canvas.get(0, 2, 1).facing());
|
||||
assertEquals("north", canvas.get(0, 2, -1).facing());
|
||||
}
|
||||
}
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package art.arcane.iris.generation.stage;
|
||||
|
||||
import art.arcane.iris.generation.biome.IrisBiome;
|
||||
import art.arcane.iris.generation.biome.IrisBiomePaletteLayer;
|
||||
import art.arcane.iris.generation.context.ChunkContext;
|
||||
import art.arcane.iris.generation.context.ChunkedDataCache;
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.generation.runtime.IrisComplex;
|
||||
import art.arcane.iris.generation.terrain.IrisSlopeClip;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
|
||||
import art.arcane.volmlib.util.hunk.Hunk;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.volmlib.util.stream.ProceduralStream;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyDouble;
|
||||
import static org.mockito.Mockito.CALLS_REAL_METHODS;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class IrisPostModifierSlabSupportTest {
|
||||
private static final int SURFACE = 12;
|
||||
private static final int PLANE_WIDTH = 5;
|
||||
private static final NativeBlockState AIR = state("minecraft:air", false, true, false);
|
||||
private static final NativeBlockState WATER = state("minecraft:water", false, false, true);
|
||||
private static final NativeBlockState STONE = state("minecraft:stone", true, false, false);
|
||||
private static final NativeBlockState SLAB = state("minecraft:cobblestone_slab", true, false, false);
|
||||
|
||||
@Test
|
||||
public void supportedStepReceivesSmoothingBlock() throws ReflectiveOperationException {
|
||||
assertSame(SLAB, smoothedBlock(STONE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void carvedAwaySurfaceDoesNotReceiveFloatingSmoothingBlock() throws ReflectiveOperationException {
|
||||
assertSame(AIR, smoothedBlock(AIR));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fluidSurfaceDoesNotSupportSmoothingBlock() throws ReflectiveOperationException {
|
||||
assertSame(AIR, smoothedBlock(WATER));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private NativeBlockState smoothedBlock(NativeBlockState support) throws ReflectiveOperationException {
|
||||
Hunk<NativeBlockState> output = Hunk.newHunk(3, 24, 3);
|
||||
for (int x = 0; x < 3; x++) {
|
||||
for (int z = 0; z < 3; z++) {
|
||||
for (int y = 0; y < output.getHeight(); y++) {
|
||||
output.set(x, y, z, y <= SURFACE ? STONE : AIR);
|
||||
}
|
||||
}
|
||||
}
|
||||
output.set(1, SURFACE, 1, support);
|
||||
output.set(2, SURFACE + 1, 1, STONE);
|
||||
|
||||
IrisBiomePaletteLayer slabLayer = mock(IrisBiomePaletteLayer.class);
|
||||
doReturn(new IrisSlopeClip()).when(slabLayer).getSlopeCondition();
|
||||
doReturn(SLAB).when(slabLayer).get(any(RNG.class), anyDouble(), anyDouble(), anyDouble(), any());
|
||||
IrisBiome biome = mock(IrisBiome.class);
|
||||
doReturn(slabLayer).when(biome).getSlab();
|
||||
ChunkedDataCache<IrisBiome> biomes = mock(ChunkedDataCache.class);
|
||||
doReturn(biome).when(biomes).get(1, 1);
|
||||
ChunkContext context = mock(ChunkContext.class);
|
||||
doReturn(biomes).when(context).getBiome();
|
||||
|
||||
ProceduralStream<Double> zeroStream = mock(ProceduralStream.class);
|
||||
doReturn(0D).when(zeroStream).get(anyDouble(), anyDouble());
|
||||
IrisComplex complex = mock(IrisComplex.class);
|
||||
doReturn(zeroStream).when(complex).getRiverCarveWeightStream();
|
||||
doReturn(zeroStream).when(complex).getRiverWaterSurfaceStream();
|
||||
doReturn(zeroStream).when(complex).getSlopeStream();
|
||||
Engine engine = mock(Engine.class);
|
||||
doReturn(complex).when(engine).getComplex();
|
||||
doReturn(mock(IrisData.class)).when(engine).getData();
|
||||
|
||||
IrisPostModifier modifier = mock(IrisPostModifier.class, CALLS_REAL_METHODS);
|
||||
doReturn(engine).when(modifier).getEngine();
|
||||
Field rng = IrisPostModifier.class.getDeclaredField("rng");
|
||||
rng.setAccessible(true);
|
||||
rng.set(modifier, new RNG(88L));
|
||||
int[] heights = new int[PLANE_WIDTH * PLANE_WIDTH];
|
||||
Arrays.fill(heights, SURFACE);
|
||||
heights[2 * PLANE_WIDTH + 3] = SURFACE + 1;
|
||||
|
||||
Method post = IrisPostModifier.class.getDeclaredMethod("post", int.class, int.class, Hunk.class,
|
||||
int.class, int.class, ChunkContext.class, int[].class, int.class, boolean.class, boolean.class);
|
||||
post.setAccessible(true);
|
||||
post.invoke(modifier, 1, 1, output, 1, 1, context, heights, PLANE_WIDTH, false, true);
|
||||
return output.get(1, SURFACE + 1, 1);
|
||||
}
|
||||
|
||||
private static NativeBlockState state(String key, boolean solid, boolean air, boolean fluid) {
|
||||
NativeBlockState state = mock(NativeBlockState.class);
|
||||
doReturn(key).when(state).key();
|
||||
doReturn(key).when(state).materialKey();
|
||||
doReturn(solid).when(state).isSolid();
|
||||
doReturn(air).when(state).isAir();
|
||||
doReturn(fluid).when(state).isFluid();
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public class SchemaBuilderIdentityTest {
|
||||
@ClassRule
|
||||
public static final PlatformLeakGuard PLATFORM_GUARD = PlatformLeakGuard.clean();
|
||||
|
||||
private static final String EXPECTED_SCHEMA_DIGEST = "91c36f3cdb0a10a0700b62fb6a6d2456576bd50f75a4d588103acdd42a7763c1";
|
||||
private static final String EXPECTED_SCHEMA_DIGEST = "7950fa5a3f3abf7fdd2cea1bd4b1fd335745b348ce64e67404cc86e898d18f16";
|
||||
|
||||
private static final List<Class<?>> SCHEMA_ROOTS = List.of(
|
||||
IrisDimension.class,
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
package art.arcane.iris.studio.edit;
|
||||
|
||||
import art.arcane.iris.generation.biome.IrisBiome;
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.localization.IrisLanguage;
|
||||
import art.arcane.iris.localization.RuntimeUiMessages;
|
||||
import art.arcane.iris.platform.bukkit.nms.INMS;
|
||||
import art.arcane.iris.platform.bukkit.nms.INMSBinding;
|
||||
import art.arcane.iris.platform.bukkit.plugin.VolmitSender;
|
||||
import art.arcane.iris.platform.generation.PlatformChunkGenerator;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.world.IrisToolbelt;
|
||||
import art.arcane.iris.world.history.SavedBiomeUnavailableException;
|
||||
import art.arcane.iris.world.task.J;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
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.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class DustRevealerTest {
|
||||
@Test
|
||||
public void loadingSurfaceBiomeSendsOneNoticeAndStopsTheReveal() {
|
||||
try (Fixture fixture = new Fixture()) {
|
||||
when(fixture.engine.getSurfaceBiome(192, 816))
|
||||
.thenThrow(new SavedBiomeUnavailableException("Saved biome information is loading.", true));
|
||||
|
||||
DustRevealer.spawn(fixture.block, fixture.sender);
|
||||
|
||||
verify(fixture.sender).sendMessage(IrisLanguage.text(RuntimeUiMessages.DUST_BIOME_LOADING));
|
||||
verifyNoMoreInteractions(fixture.sender);
|
||||
verify(fixture.engine).getObjectPlacementKey(192, 96, 816);
|
||||
fixture.verifyNoLaterQueriesOrReveal();
|
||||
fixture.logging.verifyNoInteractions();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadingDuringHeightLookupSendsOneNoticeBeforeObjectLookup() {
|
||||
try (Fixture fixture = new Fixture()) {
|
||||
when(fixture.engine.getHeight(192, 816, true))
|
||||
.thenThrow(new SavedBiomeUnavailableException("Saved biome information is loading.", true));
|
||||
|
||||
DustRevealer.spawn(fixture.block, fixture.sender);
|
||||
|
||||
verify(fixture.sender).sendMessage(IrisLanguage.text(RuntimeUiMessages.DUST_BIOME_LOADING));
|
||||
verifyNoMoreInteractions(fixture.sender);
|
||||
verify(fixture.engine, never()).getObjectPlacementKey(anyInt(), anyInt(), anyInt());
|
||||
fixture.verifyNoLaterQueriesOrReveal();
|
||||
fixture.logging.verifyNoInteractions();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anotherClickReportsTheBiomeAfterLoadingFinishes() {
|
||||
try (Fixture fixture = new Fixture()) {
|
||||
IrisBiome biome = mock(IrisBiome.class);
|
||||
when(biome.getLoadKey()).thenReturn("ready-biome");
|
||||
when(fixture.engine.getSurfaceBiome(192, 816))
|
||||
.thenThrow(new SavedBiomeUnavailableException("Saved biome information is loading.", true))
|
||||
.thenReturn(biome);
|
||||
|
||||
DustRevealer.spawn(fixture.block, fixture.sender);
|
||||
verify(fixture.sender).sendMessage(IrisLanguage.text(RuntimeUiMessages.DUST_BIOME_LOADING));
|
||||
clearInvocations(fixture.sender);
|
||||
|
||||
DustRevealer.spawn(fixture.block, fixture.sender);
|
||||
|
||||
ArgumentCaptor<String> messages = ArgumentCaptor.forClass(String.class);
|
||||
verify(fixture.sender, atLeastOnce()).sendMessage(messages.capture());
|
||||
assertTrue(messages.getAllValues().stream().anyMatch(message -> message.contains("ready-biome")));
|
||||
verify(fixture.sender, never()).sendMessage(IrisLanguage.text(RuntimeUiMessages.DUST_BIOME_LOADING));
|
||||
verify(fixture.sender, never()).sendMessage(IrisLanguage.text(RuntimeUiMessages.DUST_REVEAL_FAILED));
|
||||
fixture.logging.verifyNoInteractions();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unavailableSavedBiomeReportsTheOriginalFailureAndStopsTheReveal() {
|
||||
try (Fixture fixture = new Fixture()) {
|
||||
SavedBiomeUnavailableException failure = new SavedBiomeUnavailableException(
|
||||
"Saved biome data could not be read.", new IllegalStateException("Corrupt biome receipt"));
|
||||
when(fixture.engine.getSurfaceBiome(192, 816)).thenThrow(failure);
|
||||
|
||||
DustRevealer.spawn(fixture.block, fixture.sender);
|
||||
|
||||
fixture.logging.verify(() -> IrisLogging.reportError(failure));
|
||||
fixture.logging.verifyNoMoreInteractions();
|
||||
verify(fixture.sender).sendMessage(IrisLanguage.text(RuntimeUiMessages.DUST_REVEAL_FAILED));
|
||||
verifyNoMoreInteractions(fixture.sender);
|
||||
verify(fixture.engine).getObjectPlacementKey(192, 96, 816);
|
||||
fixture.verifyNoLaterQueriesOrReveal();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Fixture implements AutoCloseable {
|
||||
private final MockedStatic<IrisToolbelt> toolbelt = mockStatic(IrisToolbelt.class);
|
||||
private final MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class);
|
||||
private final MockedStatic<INMS> nativeAccess = mockStatic(INMS.class);
|
||||
private final MockedStatic<J> scheduling = mockStatic(J.class);
|
||||
private final Engine engine = mock(Engine.class);
|
||||
private final World world = mock(World.class);
|
||||
private final Block block = mock(Block.class);
|
||||
private final VolmitSender sender = mock(VolmitSender.class);
|
||||
|
||||
private Fixture() {
|
||||
PlatformChunkGenerator generator = mock(PlatformChunkGenerator.class);
|
||||
when(generator.getEngine()).thenReturn(engine);
|
||||
toolbelt.when(() -> IrisToolbelt.access(world)).thenReturn(generator);
|
||||
INMSBinding binding = mock(INMSBinding.class);
|
||||
when(binding.getTrueBiomeBaseKey(any())).thenReturn("minecraft:plains");
|
||||
nativeAccess.when(INMS::get).thenReturn(binding);
|
||||
when(block.getWorld()).thenReturn(world);
|
||||
when(block.getX()).thenReturn(192);
|
||||
when(block.getY()).thenReturn(96);
|
||||
when(block.getZ()).thenReturn(816);
|
||||
when(block.getType()).thenReturn(Material.STONE);
|
||||
when(engine.getHeight(192, 816, true)).thenReturn(96);
|
||||
}
|
||||
|
||||
private void verifyNoLaterQueriesOrReveal() {
|
||||
verify(engine, never()).getBiome(anyInt(), anyInt(), anyInt());
|
||||
verify(engine, never()).getCaveOrMantleBiome(anyInt(), anyInt(), anyInt());
|
||||
verify(engine, never()).getRegion(anyInt(), anyInt());
|
||||
verify(engine, never()).getObjectsAt(anyInt(), anyInt());
|
||||
scheduling.verifyNoInteractions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
scheduling.close();
|
||||
nativeAccess.close();
|
||||
logging.close();
|
||||
toolbelt.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -28,4 +28,4 @@ apiVersion=26.1
|
||||
fabricLoaderVersion=0.19.3
|
||||
forgeVersion=26.2-65.1.1
|
||||
neoForgeVersion=26.2.0.59
|
||||
volmLibCoordinate=com.github.VolmitSoftware.VolmLib:volmlib-shared:6c54bbcd19282508364794df1eacb9720d94a896
|
||||
volmLibCoordinate=com.github.VolmitSoftware.VolmLib:volmlib-shared:d24d28acddbf3555c0c30ac99569ebacd476ce27
|
||||
|
||||
Reference in New Issue
Block a user