This commit is contained in:
Brian Neumann-Fopiano
2026-07-18 18:12:04 -04:00
parent 0193f75daa
commit ad41b6795c
48 changed files with 1193 additions and 141 deletions
+1 -1
View File
@@ -37,7 +37,7 @@ plugins {
def lib = 'art.arcane.iris.util'
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate')
.orElse('com.github.VolmitSoftware:VolmLib:4c38988b344792a79f925f57f8a675bc85fc1bed')
.orElse('com.github.VolmitSoftware:VolmLib:d9026a7c8ebc391c8109f401ce79a0ce65df3969')
.get()
String sentryAuthToken = findProperty('sentry.auth.token') as String ?: System.getenv('SENTRY_AUTH_TOKEN')
boolean hasSentryAuthToken = sentryAuthToken != null && !sentryAuthToken.isBlank()
@@ -163,7 +163,6 @@ final class DecoratorCore {
}
data.set(x, height, z, fixFacesForHunk(
decorator.getForceBlock().getBlockData(irisData), data, x, z, realX, height, realZ, mantle));
return;
}
if (!decorator.isForcePlace()) {
@@ -135,36 +135,42 @@ public class IrisStructureComponent extends IrisMantleComponent {
ObjectPlaceMode mode = structure.getPlaceMode();
int failedPieces = 0;
Long2IntOpenHashMap foundationColumns = placement.getStilt() == null
IrisStructureStiltSettings stilt = placement.getStilt();
Long2IntOpenHashMap foundationColumns = stilt == null
? null : new Long2IntOpenHashMap();
boolean supportNonOccluding = stilt != null && stilt.isSupportNonOccluding();
if (placement.isUnderground()) {
ObjectPlaceMode undergroundMode = (mode == ObjectPlaceMode.ORGANIC_STILT || mode == ObjectPlaceMode.CEILING_HANG)
? mode : ObjectPlaceMode.STRUCTURE_PIECE;
for (PlacedStructurePiece p : pieces) {
if (placeObject(writer, structure, p, undergroundMode, p.getY(), rng, foundationColumns) == -1) {
if (placeObject(writer, structure, p, undergroundMode, p.getY(), rng,
foundationColumns, supportNonOccluding) == -1) {
failedPieces++;
}
}
} else if (mode == ObjectPlaceMode.STRUCTURE_PIECE || mode == ObjectPlaceMode.FLOATING) {
for (PlacedStructurePiece p : pieces) {
if (placeObject(writer, structure, p, ObjectPlaceMode.STRUCTURE_PIECE, p.getY(), rng, foundationColumns) == -1) {
if (placeObject(writer, structure, p, ObjectPlaceMode.STRUCTURE_PIECE, p.getY(), rng,
foundationColumns, supportNonOccluding) == -1) {
failedPieces++;
}
}
} else if (pieces.size() == 1) {
if (placeObject(writer, structure, pieces.getFirst(), mode, -1, rng, foundationColumns) == -1) {
if (placeObject(writer, structure, pieces.getFirst(), mode, -1, rng,
foundationColumns, supportNonOccluding) == -1) {
failedPieces++;
}
} else {
for (PlacedStructurePiece p : pieces) {
if (placeObject(writer, structure, p, ObjectPlaceMode.STRUCTURE_PIECE, p.getY(), rng, foundationColumns) == -1) {
if (placeObject(writer, structure, p, ObjectPlaceMode.STRUCTURE_PIECE, p.getY(), rng,
foundationColumns, supportNonOccluding) == -1) {
failedPieces++;
}
}
}
requireAppliedPieces(resolved, cx, cz, failedPieces);
if (failedPieces == 0 && placement.getStilt() != null) {
placeFoundation(writer, foundationColumns, placement.getStilt(), rng, !placement.isUnderground());
if (failedPieces == 0 && stilt != null) {
placeFoundation(writer, foundationColumns, stilt, rng, !placement.isUnderground());
}
}
@@ -533,7 +539,8 @@ public class IrisStructureComponent extends IrisMantleComponent {
}
private int placeObject(MantleWriter writer, IrisStructure structure, PlacedStructurePiece p,
ObjectPlaceMode mode, int y, RNG rng, Long2IntOpenHashMap foundationColumns) {
ObjectPlaceMode mode, int y, RNG rng, Long2IntOpenHashMap foundationColumns,
boolean supportNonOccluding) {
IrisObject object = p.getObject();
String objectKey = object.getLoadKey();
IrisObjectPlacement config = structure.createLootPlacement(objectKey);
@@ -549,7 +556,8 @@ public class IrisStructureComponent extends IrisMantleComponent {
String marker = structurePlacementMarker(structure, p, objectKey);
return object.place(p.getX(), placeY, p.getZ(), writer, config, rng, (position, state) -> {
StructureFoundationPlanner.recordBaseCell(
foundationColumns, position.getX(), position.getY(), position.getZ(), state);
foundationColumns, position.getX(), position.getY(), position.getZ(), state,
supportNonOccluding);
if (marker != null && shouldWriteStructureMarker(state)) {
writer.setData(position.getX(), position.getY(), position.getZ(), marker);
}
@@ -13,8 +13,9 @@ final class StructureFoundationPlanner {
}
static void recordBaseCell(Long2IntOpenHashMap columns, int x, int y, int z,
PlatformBlockState state) {
if (columns == null || state == null || !state.isOccluding()) {
PlatformBlockState state, boolean supportNonOccluding) {
if (columns == null || state == null
|| !(supportNonOccluding ? state.isSolid() : state.isOccluding())) {
return;
}
long columnKey = pack(x, z);
@@ -24,6 +24,7 @@ import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisDepositGenerator;
import art.arcane.iris.engine.object.IrisDepositVariant;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.engine.object.IrisDimensionCarvingResolver;
import art.arcane.iris.engine.object.IrisObject;
import art.arcane.iris.engine.object.IrisProceduralBlocks;
import art.arcane.iris.engine.object.IrisRegion;
@@ -87,6 +88,7 @@ public class IrisDepositModifier extends EngineAssignedModifier<PlatformBlockSta
}
public void generate(IrisDepositGenerator k, MantleChunk chunk, Hunk<PlatformBlockState> data, RNG rng, int cx, int cz, boolean safe, HeightMap he, ChunkContext context) {
IrisDimensionCarvingResolver.State carvingState = new IrisDimensionCarvingResolver.State();
if (k.getSpawnChance() < rng.d())
return;
@@ -123,6 +125,29 @@ public class IrisDepositModifier extends EngineAssignedModifier<PlatformBlockSta
if (y > k.getMaxHeight() || y < k.getMinHeight() || y > height - 2)
continue;
if (k.isOre(getData())) {
IrisBiome depositBiome = getEngine().getCaveBiome(
(cx << 4) + x, y, (cz << 4) + z, carvingState);
if (depositBiome != null) {
double frequencyMultiplier = depositBiome.getOreDepositFrequencyMultiplier();
if (frequencyMultiplier < 1D
&& !passesOreFrequency(frequencyMultiplier, rng.d())) {
continue;
}
double sizeMultiplier = depositBiome.getOreDepositSizeMultiplier();
if (sizeMultiplier != 1D) {
IrisObject scaledClump = k.getClump(getEngine(), rng, getData(), sizeMultiplier);
int scaledDimension = scaledClump.getW();
x = clampDepositCenter(x, scaledDimension, 16);
y = clampDepositCenter(y, scaledDimension, getEngine().getHeight());
z = clampDepositCenter(z, scaledDimension, 16);
clump = scaledClump;
}
}
}
IrisDimension dimension = getDimension();
for (art.arcane.iris.util.common.math.IrisBlockVector j : clump.getBlocks().keys()) {
@@ -148,7 +173,8 @@ public class IrisDepositModifier extends EngineAssignedModifier<PlatformBlockSta
if (chunk.get(nx, ny, nz, MatterCavern.class) == null) {
PlatformBlockState ore = clump.getBlocks().get(j);
PlatformBlockState remapped = resolveDepositVariant(cx, cz, nx, ny, nz, ore, dimension, context);
PlatformBlockState remapped = resolveDepositVariant(
cx, cz, nx, ny, nz, ore, dimension, context, carvingState);
PlatformBlockState finalBlock = remapped != null
? remapped
: B.toDeepSlateOre(current, ore);
@@ -177,12 +203,22 @@ public class IrisDepositModifier extends EngineAssignedModifier<PlatformBlockSta
return state != null && !state.isAir() && !state.isFluid();
}
private PlatformBlockState resolveDepositVariant(int cx, int cz, int nx, int localY, int nz, PlatformBlockState ore, IrisDimension dimension, ChunkContext context) {
static boolean passesOreFrequency(double multiplier, double sample) {
return multiplier >= 1D || sample < Math.max(0D, multiplier);
}
static int clampDepositCenter(int center, int dimension, int limit) {
int minimum = dimension / 2;
int maximum = (int) (limit - dimension / 2D);
return Math.max(minimum, Math.min(center, maximum));
}
private PlatformBlockState resolveDepositVariant(int cx, int cz, int nx, int localY, int nz, PlatformBlockState ore, IrisDimension dimension, ChunkContext context, IrisDimensionCarvingResolver.State carvingState) {
int worldX = (cx << 4) + nx;
int worldZ = (cz << 4) + nz;
int worldY = absoluteWorldY(getEngine().getMinHeight(), localY);
IrisBiome biome = getEngine().getBiome(worldX, localY, worldZ);
IrisBiome biome = getEngine().getCaveBiome(worldX, localY, worldZ, carvingState);
if (biome != null) {
PlatformBlockState match = matchDepositVariant(biome.getDepositVariants(), ore, worldY);
if (match != null) {
@@ -205,6 +205,14 @@ public class IrisBiome extends IrisRegistrant implements IRare {
@ArrayType(min = 1, type = IrisDepositGenerator.class)
@Desc("Define biome deposit generators that add onto the existing regional and global deposit generators")
private KList<IrisDepositGenerator> deposits = new KList<>();
@MinNumber(0)
@MaxNumber(1)
@Desc("Multiplier applied to the frequency of every ore deposit whose center is inside this biome. A value of 0.4 keeps 40% of ore veins while leaving non-ore deposits unchanged.")
private double oreDepositFrequencyMultiplier = 1D;
@MinNumber(0.01)
@MaxNumber(16)
@Desc("Multiplier applied to the block count of every ore deposit whose center is inside this biome. Non-ore deposits are unchanged.")
private double oreDepositSizeMultiplier = 1D;
@ArrayType(min = 1, type = IrisDepositVariant.class)
@Desc("Deposit ore remap rules scoped to this biome. Each entry declares a vertical band and a source->replacement block id map. Applied before regional and dimension rules; first matching biome rule wins.")
private KList<IrisDepositVariant> depositVariants = new KList<>();
@@ -27,16 +27,18 @@ import art.arcane.iris.engine.object.annotations.MaxNumber;
import art.arcane.iris.engine.object.annotations.MinNumber;
import art.arcane.iris.engine.object.annotations.Required;
import art.arcane.iris.engine.object.annotations.Snippet;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.math.IrisBlockVector;
import art.arcane.iris.util.common.math.Vector3i;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KSet;
import art.arcane.volmlib.util.math.BlockPosition;
import art.arcane.volmlib.util.math.RNG;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import art.arcane.iris.spi.PlatformBlockState;
import lombok.experimental.Accessors;
@Snippet("deposit")
@@ -46,8 +48,10 @@ import lombok.experimental.Accessors;
@Desc("Creates ore & other block deposits underground")
@Data
public class IrisDepositGenerator {
private final transient AtomicCache<KList<IrisObject>> objects = new AtomicCache<>();
private final transient ConcurrentMap<ClumpCacheKey, KList<IrisObject>> objects = new ConcurrentHashMap<>();
private final transient AtomicCache<KList<PlatformBlockState>> blockData = new AtomicCache<>();
private final transient AtomicCache<Boolean> ore = new AtomicCache<>();
private final transient ConcurrentMap<ClumpCacheKey, KList<IrisObject>> scaledObjects = new ConcurrentHashMap<>();
@Required
@MinNumber(0)
@MaxNumber(8192) // TODO: WARNING HEIGHT
@@ -98,13 +102,14 @@ public class IrisDepositGenerator {
private boolean replaceBedrock = false;
public IrisObject getClump(Engine engine, RNG rng, IrisData rdata) {
KList<IrisObject> objects = this.objects.aquire(() ->
{
RNG rngv = new RNG(engine.getSeedManager().getDeposit() + hashCode());
ClumpCacheKey cacheKey = new ClumpCacheKey(engine.getSeedManager().getDeposit(), minSize, maxSize);
KList<IrisObject> objects = this.objects.computeIfAbsent(cacheKey, key -> {
RNG rngv = new RNG(key.depositSeed() + hashCode());
KList<IrisObject> objectsf = new KList<>();
for (int i = 0; i < varience; i++) {
objectsf.add(generateClumpObject(rngv.nextParallelRNG(2349 * i + 3598), rdata));
objectsf.add(generateClumpObject(
rngv.nextParallelRNG(2349 * i + 3598), rdata, key.minSize(), key.maxSize()));
}
return objectsf;
@@ -112,12 +117,40 @@ public class IrisDepositGenerator {
return objects.get(rng.i(0, objects.size()));
}
public IrisObject getClump(Engine engine, RNG rng, IrisData rdata, double sizeMultiplier) {
if (sizeMultiplier == 1D) {
return getClump(engine, rng, rdata);
}
int scaledMinSize = scaledDepositSize(minSize, sizeMultiplier);
int scaledMaxSize = scaledDepositSize(maxSize, sizeMultiplier);
ClumpCacheKey cacheKey = new ClumpCacheKey(
engine.getSeedManager().getDeposit(), scaledMinSize, scaledMaxSize);
KList<IrisObject> objects = scaledObjects.computeIfAbsent(cacheKey, key -> {
long sizeSeed = ((long) key.minSize() << 32) ^ (key.maxSize() & 0xffffffffL);
RNG rngv = new RNG(key.depositSeed() + hashCode() + sizeSeed);
KList<IrisObject> generated = new KList<>();
for (int i = 0; i < varience; i++) {
generated.add(generateClumpObject(
rngv.nextParallelRNG(2349 * i + 3598), rdata, key.minSize(), key.maxSize()));
}
return generated;
});
return objects.get(rng.i(0, objects.size()));
}
public int getMaxDimension() {
return Math.min(11, (int) Math.ceil(Math.cbrt(maxSize)));
}
private IrisObject generateClumpObject(RNG rngv, IrisData rdata) {
int s = rngv.i(minSize, maxSize + 1);
static int scaledDepositSize(int size, double multiplier) {
return Math.max(0, Math.min(8192, (int) Math.round(size * multiplier)));
}
private IrisObject generateClumpObject(RNG rngv, IrisData rdata, int clumpMinSize, int clumpMaxSize) {
int s = rngv.i(clumpMinSize, clumpMaxSize + 1);
if (s == 1) {
IrisObject o = new IrisObject(1, 1, 1);
Vector3i center = o.getCenter();
@@ -184,4 +217,19 @@ public class IrisDepositGenerator {
return blockData;
});
}
public boolean isOre(IrisData rdata) {
return ore.aquire(() -> {
for (PlatformBlockState block : getBlockData(rdata)) {
if (block.isOre()) {
return true;
}
}
return false;
});
}
record ClumpCacheKey(long depositSeed, int minSize, int maxSize) {
}
}
@@ -69,6 +69,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
@@ -100,7 +101,7 @@ public class IrisObject extends IrisRegistrant {
protected transient volatile boolean smartBored = false;
@Setter
protected transient AtomicCache<AxisAlignedBB> aabb = new AtomicCache<>();
private transient final AtomicCache<Boolean> treeBlockPresence = new AtomicCache<>();
private transient final AtomicCache<KList<IrisBlockVector>> surfaceSupportOffsets = new AtomicCache<>();
@Getter
private VectorMap<PlatformBlockState> blocks;
@Getter
@@ -326,7 +327,7 @@ public class IrisObject extends IrisRegistrant {
}
public void readLegacy(InputStream in) throws IOException {
treeBlockPresence.reset();
surfaceSupportOffsets.reset();
DataInputStream din = new DataInputStream(in);
this.w = din.readInt();
this.h = din.readInt();
@@ -358,7 +359,7 @@ public class IrisObject extends IrisRegistrant {
}
public void read(InputStream in) throws Throwable {
treeBlockPresence.reset();
surfaceSupportOffsets.reset();
DataInputStream din = new DataInputStream(in);
this.w = din.readInt();
this.h = din.readInt();
@@ -602,6 +603,7 @@ public class IrisObject extends IrisRegistrant {
shrinkOffset = offset;
blocks = b;
states = s;
surfaceSupportOffsets.reset();
}
public void clean() {
@@ -613,6 +615,7 @@ public class IrisObject extends IrisRegistrant {
blocks = d;
states = dx;
surfaceSupportOffsets.reset();
}
public IrisBlockVector getSigned(int x, int y, int z) {
@@ -624,7 +627,7 @@ public class IrisObject extends IrisRegistrant {
}
public void setUnsigned(int x, int y, int z, PlatformBlockState block) {
treeBlockPresence.reset();
surfaceSupportOffsets.reset();
IrisBlockVector v = getSigned(x, y, z);
if (block == null) {
@@ -646,7 +649,7 @@ public class IrisObject extends IrisRegistrant {
}
public void setUnsigned(int x, int y, int z, Block block, boolean legacy) {
treeBlockPresence.reset();
surfaceSupportOffsets.reset();
IrisBlockVector v = getSigned(x, y, z);
if (block == null) {
@@ -932,9 +935,8 @@ public class IrisObject extends IrisRegistrant {
&& !config.isUnderwater()
&& !config.isOnwater()
&& config.getCarvingSupport().supportsSurface()
&& hasTreeBlocks()
&& IrisSurfaceOpening.isOpen(oplacer, getLoader(), x, z, config.getTranslate(),
config.getRotation(), spinx, spiny, spinz)) {
config.getRotation(), spinx, spiny, spinz, getSurfaceSupportOffsets())) {
return -1;
}
@@ -1511,16 +1513,32 @@ public class IrisObject extends IrisRegistrant {
};
}
boolean hasTreeBlocks() {
Boolean present = treeBlockPresence.aquire(() -> {
KList<IrisBlockVector> getSurfaceSupportOffsets() {
return surfaceSupportOffsets.aquire(() -> {
readLock.lock();
try {
return IrisSurfaceOpening.containsTreeBlocks(blocks.values());
int lowestY = Integer.MAX_VALUE;
KList<IrisBlockVector> offsets = new KList<>();
for (Map.Entry<IrisBlockVector, PlatformBlockState> entry : blocks) {
PlatformBlockState state = entry.getValue();
if (state == null || !state.isSolid() || state.isFoliage()) {
continue;
}
IrisBlockVector position = entry.getKey();
int blockY = position.getBlockY();
if (blockY < lowestY) {
lowestY = blockY;
offsets.clear();
}
if (blockY == lowestY) {
offsets.add(position.clone());
}
}
return offsets;
} finally {
readLock.unlock();
}
});
return Boolean.TRUE.equals(present);
}
private boolean isCarvedCaveAnchor(IObjectPlacer placer, int x, int y, int z) {
@@ -1584,6 +1602,7 @@ public class IrisObject extends IrisRegistrant {
blocks = d;
states = dx;
surfaceSupportOffsets.reset();
shrinkwrap();
writeLock.unlock();
}
@@ -1713,6 +1732,7 @@ public class IrisObject extends IrisRegistrant {
}
blocks = b;
surfaceSupportOffsets.reset();
writeLock.unlock();
}
@@ -1744,6 +1764,7 @@ public class IrisObject extends IrisRegistrant {
}
blocks = b;
surfaceSupportOffsets.reset();
writeLock.unlock();
}
@@ -1779,6 +1800,7 @@ public class IrisObject extends IrisRegistrant {
}
blocks = b;
surfaceSupportOffsets.reset();
writeLock.unlock();
}
@@ -21,4 +21,7 @@ public class IrisStructureStiltSettings {
@Desc("Block palette used for foundation columns.")
private IrisMaterialPalette palette = new IrisMaterialPalette().qclear().qadd("minecraft:cobblestone");
@Desc("For Iris-authored structures, whether solid partial blocks such as slabs, stairs, and walls may seed foundation columns instead of requiring a fully occluding base block.")
private boolean supportNonOccluding = false;
}
@@ -1,9 +1,10 @@
package art.arcane.iris.engine.object;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.math.IrisBlockVector;
import java.util.List;
final class IrisSurfaceOpening {
private static final int SUPPORT_RADIUS = 1;
@@ -11,15 +12,37 @@ final class IrisSurfaceOpening {
}
static boolean isOpen(IObjectPlacer placer, IrisData data, int x, int z, IrisObjectTranslate translate,
IrisObjectRotation rotation, int spinX, int spinY, int spinZ) {
IrisBlockVector offset = new IrisBlockVector(0, 0, 0);
if (translate != null) {
offset = rotation == null
? translate.translate(offset)
: translate.translate(offset, rotation, spinX, spinY, spinZ);
IrisObjectRotation rotation, int spinX, int spinY, int spinZ,
List<IrisBlockVector> supportOffsets) {
if (supportOffsets.isEmpty()) {
IrisBlockVector origin = transform(new IrisBlockVector(0, 0, 0), translate, rotation, spinX, spinY, spinZ);
return isOpenAround(placer, data, x + origin.getBlockX(), z + origin.getBlockZ());
}
int centerX = x + offset.getBlockX();
int centerZ = z + offset.getBlockZ();
for (IrisBlockVector supportOffset : supportOffsets) {
IrisBlockVector transformed = transform(supportOffset, translate, rotation, spinX, spinY, spinZ);
if (isOpenAround(placer, data, x + transformed.getBlockX(), z + transformed.getBlockZ())) {
return true;
}
}
return false;
}
private static IrisBlockVector transform(IrisBlockVector offset, IrisObjectTranslate translate,
IrisObjectRotation rotation, int spinX, int spinY, int spinZ) {
IrisBlockVector transformed = offset.clone();
if (rotation != null) {
transformed = rotation.rotate(transformed, spinX, spinY, spinZ);
}
if (translate == null) {
return transformed;
}
return rotation == null
? translate.translate(transformed)
: translate.translate(transformed, rotation, spinX, spinY, spinZ);
}
private static boolean isOpenAround(IObjectPlacer placer, IrisData data, int centerX, int centerZ) {
for (int dx = -SUPPORT_RADIUS; dx <= SUPPORT_RADIUS; dx++) {
for (int dz = -SUPPORT_RADIUS; dz <= SUPPORT_RADIUS; dz++) {
int sampleX = centerX + dx;
@@ -32,13 +55,4 @@ final class IrisSurfaceOpening {
}
return false;
}
static boolean containsTreeBlocks(Iterable<PlatformBlockState> states) {
for (PlatformBlockState state : states) {
if (state != null && state.isTreeBlock()) {
return true;
}
}
return false;
}
}
@@ -158,6 +158,7 @@ public class SchemaBuilderParityTest {
assertTrue(properties.has("distribution"));
assertEquals("object", stilt.getString("type"));
assertTrue(stiltProperties.has("palette"));
assertTrue(stiltProperties.has("supportNonOccluding"));
assertEquals(1, maxDepth.getInt("minimum"));
assertEquals(4064, maxDepth.getInt("maximum"));
assertFalse(properties.has("rotation"));
@@ -2,6 +2,7 @@ package art.arcane.iris.engine.decorator;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisBlockData;
import art.arcane.iris.engine.object.IrisDecorationPart;
import art.arcane.iris.engine.object.IrisDecorator;
import art.arcane.iris.spi.PlatformBlockState;
@@ -378,6 +379,31 @@ public class DecoratorCoreTest {
assertSame(upper, output.get(0, 2, 0));
}
@Test
public void forcedSurfaceBlockStillPlacesDecorantAboveIt() {
IrisDecorator decorator = mock(IrisDecorator.class);
IrisBlockData forcedBlock = mock(IrisBlockData.class);
IrisData data = mock(IrisData.class);
PlatformBlockState support = sturdyState();
PlatformBlockState farmland = sturdyState();
PlatformBlockState air = airState();
PlatformBlockState wheat = mock(PlatformBlockState.class);
when(wheat.key()).thenReturn("minecraft:wheat[age=7]");
when(decorator.getForceBlock()).thenReturn(forcedBlock);
when(forcedBlock.getBlockData(data)).thenReturn(farmland);
when(decorator.pickBlockData(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(wheat);
Hunk<PlatformBlockState> output = Hunk.newArrayHunk(1, 3, 1);
output.set(0, 0, 0, support);
output.set(0, 1, 0, air);
DecoratorCore.placeSurfaceSingle(
decorator, 0, 0, 0, 0, 0, output, new RNG(1L), data, false, false, null);
assertSame(farmland, output.get(0, 0, 0));
assertSame(wheat, output.get(0, 1, 0));
}
private PlatformBlockState airState() {
PlatformBlockState air = mock(PlatformBlockState.class);
when(air.isAir()).thenReturn(true);
@@ -15,26 +15,52 @@ import static org.mockito.Mockito.when;
public class StructureFoundationPlannerTest {
@Test
public void recordsLowestPlacedOccludingCellAcrossTheAssembledFootprint() {
public void recordsLowestPlacedOccludingCellByDefault() {
PlatformBlockState solid = mock(PlatformBlockState.class);
when(solid.isOccluding()).thenReturn(true);
when(solid.isSolid()).thenReturn(true);
PlatformBlockState nonOccludingSolid = mock(PlatformBlockState.class);
when(nonOccludingSolid.isSolid()).thenReturn(true);
when(nonOccludingSolid.isOccluding()).thenReturn(false);
PlatformBlockState decoration = mock(PlatformBlockState.class);
when(decoration.isOccluding()).thenReturn(false);
Long2IntOpenHashMap columns = new Long2IntOpenHashMap();
StructureFoundationPlanner.recordBaseCell(columns, 100, 48, 200, decoration);
StructureFoundationPlanner.recordBaseCell(columns, 100, 49, 200, solid);
StructureFoundationPlanner.recordBaseCell(columns, 100, 50, 200, solid);
StructureFoundationPlanner.recordBaseCell(columns, 100, 45, 200, solid);
StructureFoundationPlanner.recordBaseCell(columns, -17, 60, -33, solid);
StructureFoundationPlanner.recordBaseCell(columns, 100, 48, 200, decoration, false);
StructureFoundationPlanner.recordBaseCell(columns, 100, 49, 200, solid, false);
StructureFoundationPlanner.recordBaseCell(columns, 100, 50, 200, solid, false);
StructureFoundationPlanner.recordBaseCell(columns, 100, 45, 200, solid, false);
StructureFoundationPlanner.recordBaseCell(columns, -17, 60, -33, solid, false);
StructureFoundationPlanner.recordBaseCell(columns, 12, 41, 44, solid, false);
StructureFoundationPlanner.recordBaseCell(columns, 12, 36, 44, nonOccludingSolid, false);
assertEquals(2, columns.size());
assertEquals(3, columns.size());
assertEquals(45, columns.get(StructureFoundationPlanner.pack(100, 200)));
assertEquals(60, columns.get(StructureFoundationPlanner.pack(-17, -33)));
assertEquals(41, columns.get(StructureFoundationPlanner.pack(12, 44)));
assertEquals(-17, StructureFoundationPlanner.unpackX(StructureFoundationPlanner.pack(-17, -33)));
assertEquals(-33, StructureFoundationPlanner.unpackZ(StructureFoundationPlanner.pack(-17, -33)));
}
@Test
public void recordsSolidNonOccludingBaseWhenEnabled() {
PlatformBlockState partialStructureBlock = mock(PlatformBlockState.class);
when(partialStructureBlock.isSolid()).thenReturn(true);
when(partialStructureBlock.isOccluding()).thenReturn(false);
PlatformBlockState fullStructureBlock = mock(PlatformBlockState.class);
when(fullStructureBlock.isSolid()).thenReturn(true);
when(fullStructureBlock.isOccluding()).thenReturn(true);
Long2IntOpenHashMap columns = new Long2IntOpenHashMap();
StructureFoundationPlanner.recordBaseCell(
columns, 12, 41, 44, fullStructureBlock, true);
StructureFoundationPlanner.recordBaseCell(
columns, 12, 36, 44, partialStructureBlock, true);
assertEquals(1, columns.size());
assertEquals(36, columns.get(StructureFoundationPlanner.pack(12, 44)));
}
@Test
public void scansThroughAirAndFluidUntilSolidGround() {
int groundY = StructureFoundationPlanner.findGroundY(10, 10, 0, y -> y == 3);
@@ -53,4 +53,21 @@ public class IrisDepositModifierContainmentTest {
assertFalse(IrisDepositModifier.canReplaceDepositTarget(fluid));
assertFalse(IrisDepositModifier.canReplaceDepositTarget(null));
}
@Test
public void oreFrequencyMultiplierKeepsConfiguredShareOfVeins() {
assertTrue(IrisDepositModifier.passesOreFrequency(0.4D, 0.399D));
assertFalse(IrisDepositModifier.passesOreFrequency(0.4D, 0.4D));
assertTrue(IrisDepositModifier.passesOreFrequency(1D, 0.999D));
assertFalse(IrisDepositModifier.passesOreFrequency(0D, 0D));
}
@Test
public void largerVeinsRemainCenteredAndInsideTheChunk() {
assertEquals(6, IrisDepositModifier.clampDepositCenter(6, 5, 16));
assertEquals(2, IrisDepositModifier.clampDepositCenter(1, 5, 16));
assertEquals(13, IrisDepositModifier.clampDepositCenter(14, 5, 16));
assertEquals(2, IrisDepositModifier.clampDepositCenter(1, 4, 16));
assertEquals(14, IrisDepositModifier.clampDepositCenter(15, 4, 16));
}
}
@@ -0,0 +1,64 @@
package art.arcane.iris.engine.object;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.spi.PlatformBlockState;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class IrisDepositTuningTest {
@Test
public void depositSizesScaleAndRemainWithinSchemaLimit() {
assertEquals(8, IrisDepositGenerator.scaledDepositSize(4, 2D));
assertEquals(16, IrisDepositGenerator.scaledDepositSize(8, 2D));
assertEquals(0, IrisDepositGenerator.scaledDepositSize(0, 2D));
assertEquals(8192, IrisDepositGenerator.scaledDepositSize(8192, 2D));
}
@Test
public void biomeOreTuningDefaultsPreserveExistingGeneration() {
IrisBiome biome = new IrisBiome();
assertEquals(1D, biome.getOreDepositFrequencyMultiplier(), 0D);
assertEquals(1D, biome.getOreDepositSizeMultiplier(), 0D);
}
@Test
public void clumpCachesAreScopedByWorldSeedAndSize() {
IrisDepositGenerator.ClumpCacheKey firstWorld =
new IrisDepositGenerator.ClumpCacheKey(41L, 4, 8);
IrisDepositGenerator.ClumpCacheKey secondWorld =
new IrisDepositGenerator.ClumpCacheKey(42L, 4, 8);
IrisDepositGenerator.ClumpCacheKey largerVein =
new IrisDepositGenerator.ClumpCacheKey(41L, 8, 16);
assertNotEquals(firstWorld, secondWorld);
assertNotEquals(firstWorld, largerVein);
assertEquals(firstWorld, new IrisDepositGenerator.ClumpCacheKey(41L, 4, 8));
}
@Test
public void onlyOreDepositPalettesReceiveBiomeTuning() {
IrisData data = mock(IrisData.class);
IrisDepositGenerator oreGenerator = generatorWithState(data, true);
IrisDepositGenerator stoneGenerator = generatorWithState(data, false);
assertTrue(oreGenerator.isOre(data));
assertFalse(stoneGenerator.isOre(data));
}
private IrisDepositGenerator generatorWithState(IrisData data, boolean ore) {
IrisBlockData block = mock(IrisBlockData.class);
PlatformBlockState state = mock(PlatformBlockState.class);
when(block.getBlockData(data)).thenReturn(state);
when(state.isOre()).thenReturn(ore);
IrisDepositGenerator generator = new IrisDepositGenerator();
generator.getPalette().add(block);
return generator;
}
}
@@ -7,6 +7,7 @@ import org.junit.Test;
import java.lang.reflect.Field;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
public class IrisStructureStiltSettingsTest {
@@ -15,6 +16,7 @@ public class IrisStructureStiltSettingsTest {
IrisStructureStiltSettings settings = new IrisStructureStiltSettings();
assertEquals(64, settings.getMaxDepth());
assertFalse(settings.isSupportNonOccluding());
assertNotNull(settings.getPalette());
assertEquals(1, settings.getPalette().getPalette().size());
assertEquals("minecraft:cobblestone", settings.getPalette().getPalette().get(0).getBlock());
@@ -3,6 +3,7 @@ package art.arcane.iris.engine.object;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.math.IrisBlockVector;
import org.junit.Test;
import java.util.HashMap;
@@ -23,7 +24,8 @@ public class IrisSurfaceOpeningTest {
SurfacePlacer placer = new SurfacePlacer(80);
placer.carve(1, 80, 1);
assertTrue(IrisSurfaceOpening.isOpen(placer, null, 0, 0, null, null, 0, 0, 0));
assertTrue(IrisSurfaceOpening.isOpen(placer, null, 0, 0, null, null, 0, 0, 0,
List.of(new IrisBlockVector(0, 0, 0))));
assertEquals(9, placer.heightQueries());
}
@@ -34,7 +36,8 @@ public class IrisSurfaceOpeningTest {
placer.carve(0, 78, 0);
placer.carve(0, 77, 0);
assertFalse(IrisSurfaceOpening.isOpen(placer, null, 0, 0, null, null, 0, 0, 0));
assertFalse(IrisSurfaceOpening.isOpen(placer, null, 0, 0, null, null, 0, 0, 0,
List.of(new IrisBlockVector(0, 0, 0))));
}
@Test
@@ -47,7 +50,8 @@ public class IrisSurfaceOpeningTest {
}
placer.carve(1, 82, 1);
assertTrue(IrisSurfaceOpening.isOpen(placer, null, 0, 0, null, null, 0, 0, 0));
assertTrue(IrisSurfaceOpening.isOpen(placer, null, 0, 0, null, null, 0, 0, 0,
List.of(new IrisBlockVector(0, 0, 0))));
}
@Test
@@ -55,36 +59,63 @@ public class IrisSurfaceOpeningTest {
SurfacePlacer placer = new SurfacePlacer(80);
placer.carve(2, 80, 0);
assertFalse(IrisSurfaceOpening.isOpen(placer, null, 0, 0, null, null, 0, 0, 0));
assertFalse(IrisSurfaceOpening.isOpen(placer, null, 0, 0, null, null, 0, 0, 0,
List.of(new IrisBlockVector(0, 0, 0))));
}
@Test
public void placementTranslationMovesSupportStencil() {
public void offsetSupportFootprintMovesSupportStencil() {
SurfacePlacer placer = new SurfacePlacer(80);
IrisObjectTranslate translate = new IrisObjectTranslate().setX(5).setZ(-3);
placer.carve(5, 80, -3);
assertTrue(IrisSurfaceOpening.isOpen(placer, null, 0, 0, translate, null, 0, 0, 0));
assertTrue(IrisSurfaceOpening.isOpen(placer, null, 0, 0, null, null, 0, 0, 0,
List.of(new IrisBlockVector(5, 0, -3))));
}
@Test
public void placementTranslationUsesTheSameRotationAsObjectBlocks() {
public void placementTranslationAndRotationMatchObjectBlocks() {
SurfacePlacer placer = new SurfacePlacer(80);
IrisObjectTranslate translate = new IrisObjectTranslate().setX(5).setZ(-3);
IrisObjectRotation rotation = IrisObjectRotation.of(0, 90, 0);
placer.carve(-3, 80, -5);
placer.carve(-3, 80, -7);
assertTrue(IrisSurfaceOpening.isOpen(placer, null, 0, 0, translate, rotation, 0, 0, 0));
assertTrue(IrisSurfaceOpening.isOpen(placer, null, 0, 0, translate, rotation, 0, 0, 0,
List.of(new IrisBlockVector(2, 0, 0))));
}
@Test
public void treeBlockClassificationDoesNotMatchUnrelatedObjects() {
PlatformBlockState stone = mock(PlatformBlockState.class);
PlatformBlockState log = mock(PlatformBlockState.class);
when(log.isTreeBlock()).thenReturn(true);
public void emptyFootprintFallsBackToPlacementOrigin() {
SurfacePlacer placer = new SurfacePlacer(80);
IrisObjectTranslate translate = new IrisObjectTranslate().setX(4).setZ(-2);
placer.carve(4, 80, -2);
assertFalse(IrisSurfaceOpening.containsTreeBlocks(List.of(stone)));
assertTrue(IrisSurfaceOpening.containsTreeBlocks(List.of(stone, log)));
assertTrue(IrisSurfaceOpening.isOpen(placer, null, 0, 0, translate, null, 0, 0, 0, List.of()));
}
@Test
public void objectCachesItsLowestNonFoliageSupportBlocks() {
PlatformBlockState solid = mock(PlatformBlockState.class);
PlatformBlockState foliage = mock(PlatformBlockState.class);
when(solid.isSolid()).thenReturn(true);
when(foliage.isSolid()).thenReturn(true);
when(foliage.isFoliage()).thenReturn(true);
IrisObject object = new IrisObject(9, 9, 9);
object.setUnsigned(7, 1, 4, solid);
object.setUnsigned(4, 0, 4, foliage);
object.setUnsigned(4, 5, 4, solid);
List<IrisBlockVector> initial = object.getSurfaceSupportOffsets();
assertEquals(1, initial.size());
assertEquals(3, initial.getFirst().getBlockX());
assertEquals(-3, initial.getFirst().getBlockY());
object.setUnsigned(2, 0, 4, solid);
List<IrisBlockVector> updated = object.getSurfaceSupportOffsets();
assertEquals(1, updated.size());
assertEquals(-2, updated.getFirst().getBlockX());
assertEquals(-4, updated.getFirst().getBlockY());
}
private static final class SurfacePlacer implements IObjectPlacer {