mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-27 12:41:43 +00:00
d
This commit is contained in:
+141
-78
@@ -100,13 +100,17 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sourceBeardAdjustmentsPrepareTerrainAcrossDeclaredSteps() {
|
||||
public void sourceBeardAdjustmentsPrepareOnlyAtTheSurfaceStructuresStep() {
|
||||
assertTrue(NativeStructureSurfaceFitter.shouldPrepareSurfaceTerrain(
|
||||
TerrainAdjustment.BEARD_THIN, GenerationStep.Decoration.SURFACE_STRUCTURES));
|
||||
assertTrue(NativeStructureSurfaceFitter.shouldPrepareSurfaceTerrain(
|
||||
assertFalse(NativeStructureSurfaceFitter.shouldPrepareSurfaceTerrain(
|
||||
TerrainAdjustment.BEARD_BOX, GenerationStep.Decoration.FLUID_SPRINGS));
|
||||
assertTrue(NativeStructureSurfaceFitter.shouldPrepareSurfaceTerrain(
|
||||
assertFalse(NativeStructureSurfaceFitter.shouldPrepareSurfaceTerrain(
|
||||
TerrainAdjustment.BEARD_THIN, GenerationStep.Decoration.UNDERGROUND_STRUCTURES));
|
||||
assertFalse(NativeStructureSurfaceFitter.shouldPrepareSurfaceTerrain(
|
||||
TerrainAdjustment.BEARD_BOX, GenerationStep.Decoration.UNDERGROUND_DECORATION));
|
||||
assertFalse(NativeStructureSurfaceFitter.shouldPrepareSurfaceTerrain(
|
||||
TerrainAdjustment.BEARD_THIN, GenerationStep.Decoration.STRONGHOLDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -119,6 +123,31 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
TerrainAdjustment.NONE, GenerationStep.Decoration.FLUID_SPRINGS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shallowUndergroundBeardBoxDoesNotMutateTheTopSurface() throws Exception {
|
||||
PoolElementStructurePiece piece = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(template(List.of(
|
||||
block(0, 1, 0, Blocks.DEEPSLATE_BRICKS.defaultBlockState())))),
|
||||
new BoundingBox(0, 67, 0, 0, 74, 0), 1, Rotation.NONE);
|
||||
Structure structure = new DesertPyramidStructure(
|
||||
new Structure.StructureSettings(
|
||||
HolderSet.empty(), Map.of(),
|
||||
GenerationStep.Decoration.UNDERGROUND_DECORATION,
|
||||
TerrainAdjustment.BEARD_BOX));
|
||||
StructureStart start = new StructureStart(
|
||||
structure, new ChunkPos(0, 0), 0,
|
||||
new PiecesContainer(List.of(piece)));
|
||||
BoundingBox area = new BoundingBox(0, 48, 0, 0, 80, 0);
|
||||
Map<BlockPos, BlockState> blocks = flatTerrain(area, 64);
|
||||
Map<BlockPos, BlockState> before = new HashMap<>(blocks);
|
||||
|
||||
NativeStructureSurfaceFitter.prepareSurfaceStructures(
|
||||
world(blocks), area, List.of(surfaceTarget(start)),
|
||||
(x, z) -> 64);
|
||||
|
||||
assertEquals(before, blocks);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitTerrainOverrideDisablesSourceBeardFitting() {
|
||||
StructureStart start = desertStart(TerrainAdjustment.BEARD_BOX);
|
||||
@@ -158,85 +187,70 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beardBoxUsesTheFullRigidHeightWhileBeardThinUsesTheGroundPlane() {
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor thin =
|
||||
new NativeStructureSurfaceFitter.SurfaceAnchor(0, 4, 0, 4, 64, 2);
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor box =
|
||||
new NativeStructureSurfaceFitter.SurfaceAnchor(
|
||||
0, 4, 0, 4, 64, 2, 65, 90);
|
||||
|
||||
assertEquals(80, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(thin), 2, 2, 80));
|
||||
assertEquals(64, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(box), 2, 2, 80));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beardInfluenceBeginsAtGroundYRatherThanTheSurfaceBlock() {
|
||||
public void sourceSurfaceAnchorMeetsOneBlockBelowTheAuthoredGroundPlane() {
|
||||
BoundingBox bounds = new BoundingBox(0, 64, 0, 4, 90, 4);
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor thin =
|
||||
NativeStructureSurfaceFitter.surfaceAnchor(
|
||||
bounds, 68, 2, TerrainAdjustment.BEARD_THIN);
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor box =
|
||||
NativeStructureSurfaceFitter.surfaceAnchor(
|
||||
bounds, 68, 2, TerrainAdjustment.BEARD_BOX);
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor anchor =
|
||||
NativeStructureSurfaceFitter.surfaceAnchor(bounds, 68, 2);
|
||||
|
||||
assertEquals(67, thin.meetY());
|
||||
assertEquals(0, thin.verticalDistance(68));
|
||||
assertEquals(1, thin.verticalDistance(67));
|
||||
assertEquals(0, box.verticalDistance(68));
|
||||
assertEquals(1, box.verticalDistance(67));
|
||||
assertEquals(0, box.verticalDistance(90));
|
||||
assertEquals(1, box.verticalDistance(91));
|
||||
assertEquals(67, anchor.meetY());
|
||||
assertEquals(0, anchor.minX());
|
||||
assertEquals(4, anchor.maxX());
|
||||
assertEquals(0, anchor.minZ());
|
||||
assertEquals(4, anchor.maxZ());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rigidFootprintsRaiseTerrainAcrossGapsLargerThanTheBeardKernel() {
|
||||
public void rigidFootprintsBoundTerrainAcrossGapsLargerThanTheBeardKernel() {
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor rigid = anchor(72, 2);
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor junction = anchor(72, 1);
|
||||
|
||||
assertEquals(72, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
assertEquals(46, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(rigid), 2, 2, 40));
|
||||
assertEquals(40, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
assertEquals(46, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(junction), 2, 2, 40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void elevatedRigidFootprintsBlendIntoAHorizontalRescueTaper() {
|
||||
int originalY = 40;
|
||||
int kernelEdge = NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(anchor(51, 2)), 5, 2, originalY);
|
||||
int gapThirteen = NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(anchor(52, 2)), 5, 2, originalY);
|
||||
int gapEighteen = NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(anchor(57, 2)), 5, 2, originalY);
|
||||
int gapTwentyFour = NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(anchor(63, 2)), 5, 2, originalY);
|
||||
public void beardSkirtBoundsAndGradesUpwardAndDownwardSymmetrically() {
|
||||
int originalY = 64;
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor high = anchor(96, 2);
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor low = anchor(32, 2);
|
||||
int[] raised = new int[13];
|
||||
int[] lowered = new int[13];
|
||||
for (int outset = 0; outset <= 12; outset++) {
|
||||
int x = 4 + outset;
|
||||
raised[outset] = NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(high), x, 2, originalY);
|
||||
lowered[outset] = NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(low), x, 2, originalY);
|
||||
}
|
||||
|
||||
assertEquals(originalY, kernelEdge);
|
||||
assertTrue(gapThirteen >= kernelEdge);
|
||||
assertTrue(gapEighteen > gapThirteen);
|
||||
assertTrue(gapTwentyFour > gapEighteen);
|
||||
assertTrue(gapTwentyFour < 63);
|
||||
assertEquals(originalY, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(anchor(63, 2)), 16, 2, originalY));
|
||||
assertEquals(originalY, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(anchor(63, 1)), 5, 2, originalY));
|
||||
assertEquals(80, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(anchor(64, 2)), 5, 2, 80));
|
||||
assertArrayEquals(
|
||||
new int[]{70, 69, 68, 67, 67, 66, 66, 65, 65, 64, 64, 64, 64},
|
||||
raised);
|
||||
assertArrayEquals(
|
||||
new int[]{58, 59, 60, 61, 61, 62, 62, 63, 63, 64, 64, 64, 64},
|
||||
lowered);
|
||||
for (int outset = 0; outset <= 12; outset++) {
|
||||
assertEquals(raised[outset] - originalY, originalY - lowered[outset]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectedCenterAndRigidChildrenShareTerrainSupportAcrossTheAssembly() {
|
||||
public void projectedCenterAndRigidChildrenUseProcessedFoundationCells() throws Exception {
|
||||
Structure structure = new DesertPyramidStructure(
|
||||
new Structure.StructureSettings(
|
||||
HolderSet.empty(), Map.of(),
|
||||
GenerationStep.Decoration.SURFACE_STRUCTURES,
|
||||
TerrainAdjustment.BEARD_BOX));
|
||||
PoolElementStructurePiece center = rigidPiece(
|
||||
new BoundingBox(0, 71, 0, 3, 80, 3), 1);
|
||||
PoolElementStructurePiece child = rigidPiece(
|
||||
new BoundingBox(10, 65, 0, 13, 76, 3), 7);
|
||||
PoolElementStructurePiece center = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(template(List.of(
|
||||
block(1, 1, 1, Blocks.COBBLESTONE.defaultBlockState())))),
|
||||
new BoundingBox(0, 71, 0, 3, 80, 3), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece child = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(template(List.of(
|
||||
block(1, 7, 1, Blocks.COBBLESTONE.defaultBlockState())))),
|
||||
new BoundingBox(10, 65, 0, 13, 76, 3), 7, Rotation.NONE);
|
||||
StructureStart start = new StructureStart(
|
||||
structure, new ChunkPos(0, 0), 0,
|
||||
new PiecesContainer(List.of(center, child)));
|
||||
@@ -244,8 +258,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
Map<BlockPos, BlockState> blocks = new HashMap<>();
|
||||
for (int x = area.minX(); x <= area.maxX(); x++) {
|
||||
for (int z = area.minZ(); z <= area.maxZ(); z++) {
|
||||
put(blocks, x, 63, z, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, x, 64, z, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
put(blocks, x, 64, z, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, x, 65, z, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,21 +269,66 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE)));
|
||||
NativeStructureSurfaceFitter.prepareSurfaceStructures(
|
||||
world(blocks), area, targets,
|
||||
(x, z) -> 64);
|
||||
(x, z) -> 65);
|
||||
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 1, 71, 1));
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 11, 71, 1));
|
||||
assertEquals(Blocks.DIRT.defaultBlockState(), state(blocks, 11, 70, 1));
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 2, 65, 2));
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 12, 65, 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void terrainMatchingFootprintsRaiseAndLowerOnlyLowestSolidColumns() throws Exception {
|
||||
public void sparseRigidFootprintsIgnoreAirAndSolidsAboveTheGroundPlane() throws Exception {
|
||||
StructureTemplate sparseTemplate = template(List.of(
|
||||
block(0, 1, 0, Blocks.COBBLESTONE.defaultBlockState()),
|
||||
block(15, 1, 0, Blocks.AIR.defaultBlockState()),
|
||||
block(30, 5, 0, Blocks.STONE.defaultBlockState())));
|
||||
PoolElementStructurePiece piece = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(sparseTemplate),
|
||||
new BoundingBox(0, 68, 0, 30, 76, 0), 1, Rotation.NONE);
|
||||
StructureStart start = rigidSurfaceStart(
|
||||
List.of(piece), TerrainAdjustment.BEARD_THIN);
|
||||
BoundingBox area = new BoundingBox(0, 52, 0, 30, 80, 0);
|
||||
Map<BlockPos, BlockState> blocks = flatTerrain(area, 64);
|
||||
|
||||
NativeStructureSurfaceFitter.prepareSurfaceStructures(
|
||||
world(blocks), area, List.of(surfaceTarget(start)),
|
||||
(x, z) -> 64);
|
||||
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 0, 68, 0));
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 15, 64, 0));
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 30, 64, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extremeRigidFoundationGapDoesNotCreateATerrainPillar() throws Exception {
|
||||
StructureTemplate template = template(List.of(
|
||||
block(0, 1, 0, Blocks.COBBLESTONE.defaultBlockState())));
|
||||
PoolElementStructurePiece piece = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(template),
|
||||
new BoundingBox(0, 90, 0, 0, 96, 0), 1, Rotation.NONE);
|
||||
StructureStart start = rigidSurfaceStart(
|
||||
List.of(piece), TerrainAdjustment.BEARD_THIN);
|
||||
BoundingBox area = new BoundingBox(0, 30, 0, 0, 100, 0);
|
||||
Map<BlockPos, BlockState> blocks = flatTerrain(area, 40);
|
||||
|
||||
NativeStructureSurfaceFitter.prepareSurfaceStructures(
|
||||
world(blocks), area, List.of(surfaceTarget(start)),
|
||||
(x, z) -> 40);
|
||||
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 0, 46, 0));
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, 47, 0));
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, 89, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void terrainMatchingFootprintsFollowProcessedLowestSolidColumns() throws Exception {
|
||||
StructureTemplate pathTemplate = template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState()),
|
||||
block(0, 1, 0, Blocks.DIRT_PATH.defaultBlockState()),
|
||||
block(0, 2, 0, Blocks.DIRT.defaultBlockState()),
|
||||
block(15, 0, 0, Blocks.AIR.defaultBlockState()),
|
||||
block(30, 0, 0, Blocks.AIR.defaultBlockState()),
|
||||
block(30, 1, 0, Blocks.DIRT_PATH.defaultBlockState()),
|
||||
block(30, 0, 0, Blocks.DIRT.defaultBlockState()),
|
||||
block(45, 0, 0, Blocks.DANDELION.defaultBlockState())));
|
||||
InlineSinglePoolElement element = new InlineSinglePoolElement(
|
||||
pathTemplate, List.of(), StructureTemplatePool.Projection.TERRAIN_MATCHING);
|
||||
@@ -290,8 +349,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
world(blocks), area, List.of(surfaceTarget(start)),
|
||||
(x, z) -> x == 0 ? 60 : x == 30 ? 72 : 64);
|
||||
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 0, 68, 0));
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 30, 68, 0));
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 0, 61, 0));
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 30, 71, 0));
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 30, 72, 0));
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 15, 64, 0));
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 15, 65, 0));
|
||||
@@ -770,10 +829,10 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void surfaceAnchorIsFlushInsideAndUnchangedAtRadius() {
|
||||
public void surfaceAnchorCapsInsideAndIsUnchangedAtRadius() {
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor anchor = anchor(72, 2);
|
||||
|
||||
assertEquals(72, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
assertEquals(70, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(anchor), 2, 2, 64));
|
||||
assertEquals(64, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(anchor), 16, 2, 64));
|
||||
@@ -786,7 +845,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor raised = anchor(68, 2);
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor lowered = anchor(64, 2);
|
||||
|
||||
assertEquals(64, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
assertEquals(65, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(raised), 10, 2, 64));
|
||||
assertEquals(67, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(lowered), 10, 2, 68));
|
||||
@@ -1197,16 +1256,20 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stackedRigidPiecesPreserveTheLowerAuthoredSurface() {
|
||||
public void stackedRigidPiecesPreserveTheLowerProcessedFoundation() throws Exception {
|
||||
Structure structure = new DesertPyramidStructure(
|
||||
new Structure.StructureSettings(
|
||||
HolderSet.empty(), Map.of(),
|
||||
GenerationStep.Decoration.SURFACE_STRUCTURES,
|
||||
TerrainAdjustment.BEARD_BOX));
|
||||
PoolElementStructurePiece lower = rigidPiece(
|
||||
new BoundingBox(0, 62, 0, 4, 65, 4), 1);
|
||||
PoolElementStructurePiece upper = rigidPiece(
|
||||
new BoundingBox(0, 66, 0, 4, 78, 4), 1);
|
||||
PoolElementStructurePiece lower = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(template(List.of(
|
||||
block(2, 1, 2, Blocks.COBBLESTONE.defaultBlockState())))),
|
||||
new BoundingBox(0, 62, 0, 4, 65, 4), 1, Rotation.NONE);
|
||||
PoolElementStructurePiece upper = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(template(List.of(
|
||||
block(2, 1, 2, Blocks.COBBLESTONE.defaultBlockState())))),
|
||||
new BoundingBox(0, 66, 0, 4, 78, 4), 1, Rotation.NONE);
|
||||
StructureStart start = new StructureStart(
|
||||
structure, new ChunkPos(0, 0), 0,
|
||||
new PiecesContainer(List.of(upper, lower)));
|
||||
@@ -1236,7 +1299,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
NativeStructureSurfaceFitter.SurfaceAnchor adjacent =
|
||||
new NativeStructureSurfaceFitter.SurfaceAnchor(5, 9, 0, 4, 70, 2);
|
||||
|
||||
assertEquals(65, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
assertEquals(69, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(adjacent), 4, 2, 64));
|
||||
assertEquals(66, NativeStructureSurfaceFitter.resolveSurfaceTarget(
|
||||
List.of(local, adjacent), 4, 2, 64));
|
||||
|
||||
@@ -3,7 +3,7 @@ def mainClass = 'art.arcane.iris.Iris'
|
||||
def bootstrapperClass = 'art.arcane.iris.IrisBootstrap'
|
||||
|
||||
String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate')
|
||||
.orElse('com.github.VolmitSoftware:VolmLib:5486ac97ad6d6833e27275dc768222d377721522')
|
||||
.orElse('com.github.VolmitSoftware:VolmLib:e6574cf814b3dd670385a4d632a63fde2038e186')
|
||||
.get()
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -669,7 +669,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
watch::readSettingsContent,
|
||||
watch::normalizeSettingsContent
|
||||
);
|
||||
configHotloadEngine.configure(3_000L, List.of(watch.settingsFile()), List.of());
|
||||
configHotloadEngine.configure(500L, 3_000L, List.of(watch.settingsFile()), List.of());
|
||||
// Stale-temp cleanup must complete before services enable: StudioSVC.onEnable downloads
|
||||
// packs through cache/temp on an async thread, and a concurrent delete of that folder
|
||||
// truncated pack imports mid-copy (partial packs/<key> without dimensions/).
|
||||
@@ -714,7 +714,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
pendingWorldReplacements.captureVanillaLevelContext();
|
||||
pendingWorldReplacements.verifyLoadedPublishedWorlds();
|
||||
J.a(this::bstats);
|
||||
J.ar(() -> settingsHotloadWatch.checkConfigHotload(configHotloadEngine), 60);
|
||||
J.ar(() -> settingsHotloadWatch.checkConfigHotload(configHotloadEngine), 10);
|
||||
J.sr(this::tickQueue, 0);
|
||||
J.s(this::setupPapi);
|
||||
if (IrisStartupValidation.isReady()) {
|
||||
|
||||
+38
-10
@@ -21,9 +21,12 @@ package art.arcane.iris.core;
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.localization.IrisLanguage;
|
||||
import art.arcane.volmlib.util.hotload.ConfigHotloadEngine;
|
||||
import art.arcane.volmlib.util.io.IO;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -31,6 +34,8 @@ import java.util.List;
|
||||
* {@link ConfigHotloadEngine} is built from and drains the touched-file queue.
|
||||
*/
|
||||
public final class SettingsHotloadWatch {
|
||||
private static final int MAX_SETTINGS_BYTES = 2 * 1024 * 1024;
|
||||
|
||||
private final File settingsFile;
|
||||
|
||||
public SettingsHotloadWatch(File settingsFile) {
|
||||
@@ -46,13 +51,17 @@ public final class SettingsHotloadWatch {
|
||||
return;
|
||||
}
|
||||
|
||||
for (File file : engine.pollTouchedFiles()) {
|
||||
engine.processFileChange(file, ignored -> {
|
||||
IrisSettings.invalidate();
|
||||
IrisSettings.get();
|
||||
IrisLanguage.reload();
|
||||
return true;
|
||||
}, ignored -> Iris.info("Hotloaded settings.json "));
|
||||
for (ConfigHotloadEngine.StableContentSnapshot snapshot : engine.pollTouchedSnapshots()) {
|
||||
if ("missing".equals(snapshot.signature())) {
|
||||
engine.processSnapshotChange(snapshot, ignored -> true, null);
|
||||
Iris.warn("settings.json was removed; retaining the last valid runtime settings.");
|
||||
continue;
|
||||
}
|
||||
engine.processSnapshotChange(
|
||||
snapshot,
|
||||
stable -> applySettingsSnapshot(stable.file(), stable.normalizedContent()),
|
||||
ignored -> Iris.info("Hotloaded settings.json ")
|
||||
);
|
||||
}
|
||||
IrisLanguage.update();
|
||||
}
|
||||
@@ -76,8 +85,12 @@ public final class SettingsHotloadWatch {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return IO.readAll(file);
|
||||
try (InputStream input = Files.newInputStream(file.toPath())) {
|
||||
byte[] content = input.readNBytes(MAX_SETTINGS_BYTES + 1);
|
||||
if (content.length > MAX_SETTINGS_BYTES) {
|
||||
throw new IOException("Settings exceed " + MAX_SETTINGS_BYTES + " bytes: " + file);
|
||||
}
|
||||
return new String(content, StandardCharsets.UTF_8);
|
||||
} catch (Throwable ex) {
|
||||
Iris.warn("Failed to read settings file %s: %s%s",
|
||||
file.getAbsolutePath(),
|
||||
@@ -95,4 +108,19 @@ public final class SettingsHotloadWatch {
|
||||
|
||||
return text.replace("\r\n", "\n").trim();
|
||||
}
|
||||
|
||||
private boolean applySettingsSnapshot(File file, String content) {
|
||||
if (content == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return IrisSettings.applyHotloadSnapshot(content, IrisLanguage::reload);
|
||||
} catch (RuntimeException failure) {
|
||||
Iris.warn("Rejected invalid settings hotload from %s: %s",
|
||||
file.getAbsolutePath(),
|
||||
failure.getMessage() == null ? failure.getClass().getSimpleName() : failure.getMessage());
|
||||
Iris.reportError(failure);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package art.arcane.iris.core.commands;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.core.pack.BrokenPackException;
|
||||
import art.arcane.iris.core.runtime.WorldRuntimeControlService;
|
||||
import art.arcane.iris.core.runtime.StudioOpenCoordinator;
|
||||
import art.arcane.iris.core.runtime.jigsaw.JigsawPlanarArchetype;
|
||||
@@ -960,6 +961,11 @@ public class CommandJigsaw implements DirectorExecutor {
|
||||
Throwable failure = unwrapCompletionFailure(throwable == null
|
||||
? new IllegalStateException("Studio open completed without a result")
|
||||
: throwable);
|
||||
if (isExpectedOpenDenial(failure)) {
|
||||
Iris.warn("Jigsaw Studio open for '%s' was denied: %s",
|
||||
structureKey, failure.getMessage());
|
||||
return;
|
||||
}
|
||||
Iris.reportError("Failed to open Jigsaw Studio for '" + structureKey + "'.", failure);
|
||||
sendError(commandSender, "Jigsaw Studio open failed: " + failure.getMessage());
|
||||
}
|
||||
@@ -1022,6 +1028,10 @@ public class CommandJigsaw implements DirectorExecutor {
|
||||
return current;
|
||||
}
|
||||
|
||||
static boolean isExpectedOpenDenial(Throwable failure) {
|
||||
return unwrapCompletionFailure(failure) instanceof BrokenPackException;
|
||||
}
|
||||
|
||||
private void sendError(String message) {
|
||||
sender().sendMessage(C.RED + message);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ public class WandSVC implements IrisService {
|
||||
var latch = new CountDownLatch(1);
|
||||
var holder = Iris.tickets.getHolder(p.getWorld());
|
||||
new Job() {
|
||||
private int i;
|
||||
private volatile int i;
|
||||
private Chunk chunk;
|
||||
|
||||
@Override
|
||||
|
||||
+19
-1
@@ -1,5 +1,6 @@
|
||||
package art.arcane.iris.core.commands;
|
||||
|
||||
import art.arcane.iris.core.pack.BrokenPackException;
|
||||
import art.arcane.iris.core.runtime.StudioOpenCoordinator;
|
||||
import art.arcane.iris.core.runtime.jigsaw.JigsawPlanarArchetype;
|
||||
import art.arcane.iris.core.runtime.jigsaw.JigsawStudioCellDimensions;
|
||||
@@ -7,6 +8,7 @@ import art.arcane.iris.core.runtime.jigsaw.JigsawStudioLayout;
|
||||
import art.arcane.iris.core.runtime.jigsaw.JigsawStudioMode;
|
||||
import art.arcane.iris.core.runtime.jigsaw.JigsawStudioVariantCatalog;
|
||||
import art.arcane.iris.core.runtime.jigsaw.JigsawStudioWorkcellSpec;
|
||||
import art.arcane.iris.core.service.JigsawStudioService;
|
||||
import art.arcane.iris.core.structure.authoring.StructureBackend;
|
||||
import art.arcane.iris.core.structure.authoring.StructureCapability;
|
||||
import art.arcane.iris.core.structure.authoring.StructureHash;
|
||||
@@ -17,7 +19,6 @@ import art.arcane.iris.core.structure.authoring.StructureTransactionWriter;
|
||||
import art.arcane.iris.core.structure.conversion.IrisStructureAdoptionInputKind;
|
||||
import art.arcane.iris.core.structure.export.VanillaJigsawExportFormat;
|
||||
import art.arcane.iris.core.tools.IrisCreator;
|
||||
import art.arcane.iris.core.service.JigsawStudioService;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.engine.object.IrisDirection;
|
||||
import art.arcane.iris.engine.object.IrisJigsawConnector;
|
||||
@@ -44,6 +45,7 @@ import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -101,6 +103,18 @@ public class CommandJigsawContractTest {
|
||||
CommandJigsaw.STUDIO_OPEN_KIND.datapackPreparation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void packValidationDenialsAreExpectedOpenFailures() {
|
||||
BrokenPackException failure = new BrokenPackException(
|
||||
"overworld", List.of("Biome 'broken' has no resolvable regions."));
|
||||
|
||||
assertTrue(CommandJigsaw.isExpectedOpenDenial(failure));
|
||||
assertTrue(CommandJigsaw.isExpectedOpenDenial(
|
||||
new CompletionException(failure)));
|
||||
assertFalse(CommandJigsaw.isExpectedOpenDenial(
|
||||
new IllegalStateException("Unexpected Studio failure")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void committedActivationStartsInitialEvaluationBeforePlayerBinding() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
@@ -129,6 +143,10 @@ public class CommandJigsawContractTest {
|
||||
NamespacedKey source = CommandJigsaw.parseRegisteredStructureKey("minecraft:village_plains");
|
||||
assertEquals("minecraft_village_plains", CommandJigsaw.resolveConversionTarget(source, "auto"));
|
||||
assertEquals("villages/plains", CommandJigsaw.resolveConversionTarget(source, "iris:villages/plains"));
|
||||
NamespacedKey ancientCity = CommandJigsaw.parseRegisteredStructureKey(
|
||||
"minecraft:ancient_city");
|
||||
assertEquals("minecraft_ancient_city",
|
||||
CommandJigsaw.resolveConversionTarget(ancientCity, "auto"));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> CommandJigsaw.resolveConversionTarget(source, "custom:village"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user