mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-27 04:37:47 +00:00
COntent
This commit is contained in:
+50
@@ -262,6 +262,43 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
assertEquals(Blocks.DIRT.defaultBlockState(), state(blocks, 11, 70, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void terrainMatchingFootprintsRaiseAndLowerOnlyLowestSolidColumns() throws Exception {
|
||||
StructureTemplate pathTemplate = template(List.of(
|
||||
block(0, 0, 0, Blocks.AIR.defaultBlockState()),
|
||||
block(0, 1, 0, Blocks.DIRT_PATH.defaultBlockState()),
|
||||
block(15, 0, 0, Blocks.AIR.defaultBlockState()),
|
||||
block(30, 0, 0, Blocks.AIR.defaultBlockState()),
|
||||
block(30, 1, 0, Blocks.DIRT_PATH.defaultBlockState()),
|
||||
block(45, 0, 0, Blocks.DANDELION.defaultBlockState())));
|
||||
InlineSinglePoolElement element = new InlineSinglePoolElement(
|
||||
pathTemplate, List.of(), StructureTemplatePool.Projection.TERRAIN_MATCHING);
|
||||
PoolElementStructurePiece piece = rigidTemplatePiece(
|
||||
element, new BoundingBox(0, 68, 0, 45, 74, 0), 1, Rotation.NONE);
|
||||
StructureStart start = rigidSurfaceStart(
|
||||
List.of(piece), TerrainAdjustment.BEARD_THIN);
|
||||
BoundingBox area = new BoundingBox(0, 52, 0, 45, 76, 0);
|
||||
Map<BlockPos, BlockState> blocks = flatTerrain(area, 64);
|
||||
blocks.remove(new BlockPos(0, 63, 0));
|
||||
blocks.remove(new BlockPos(0, 64, 0));
|
||||
put(blocks, 0, 59, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, 0, 60, 0, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
put(blocks, 30, 71, 0, Blocks.DIRT.defaultBlockState());
|
||||
put(blocks, 30, 72, 0, Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
|
||||
NativeStructureSurfaceFitter.prepareSurfaceStructures(
|
||||
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.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));
|
||||
assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 45, 64, 0));
|
||||
assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 45, 65, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vacuumFitsOnlyProcessedSolidTemplateColumns() throws Exception {
|
||||
StructureTemplate sparseTemplate = template(List.of(
|
||||
@@ -2171,6 +2208,19 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
if (methodName.equals("getSeed")) {
|
||||
return TEST_SEED;
|
||||
}
|
||||
if (methodName.equals("getHeight")) {
|
||||
int x = (int) arguments[1];
|
||||
int z = (int) arguments[2];
|
||||
int highest = Integer.MIN_VALUE;
|
||||
for (Map.Entry<BlockPos, BlockState> entry : blocks.entrySet()) {
|
||||
BlockPos position = entry.getKey();
|
||||
if (position.getX() == x && position.getZ() == z
|
||||
&& !entry.getValue().isAir()) {
|
||||
highest = Math.max(highest, position.getY());
|
||||
}
|
||||
}
|
||||
return highest == Integer.MIN_VALUE ? 0 : highest + 1;
|
||||
}
|
||||
if (methodName.equals("getLevel")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -93,8 +93,8 @@ import art.arcane.volmlib.util.exceptions.IrisException;
|
||||
import art.arcane.iris.util.common.format.C;
|
||||
import art.arcane.volmlib.util.function.NastyRunnable;
|
||||
import art.arcane.volmlib.util.hotload.ConfigHotloadEngine;
|
||||
import art.arcane.volmlib.util.hud.HudActionBar;
|
||||
import art.arcane.volmlib.util.hud.HudBossBarLane;
|
||||
import art.arcane.volmlib.util.hud.HudSlotService;
|
||||
import art.arcane.volmlib.util.io.IO;
|
||||
import art.arcane.volmlib.util.io.InstanceState;
|
||||
import art.arcane.volmlib.util.math.M;
|
||||
@@ -549,7 +549,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
SimdSupport.install();
|
||||
services = new KMap<>();
|
||||
setupAudience();
|
||||
BukkitPlatform.hostHud(new HudSlotService(this), new HudBossBarLane());
|
||||
BukkitPlatform.hostHud(new HudActionBar(this), new HudBossBarLane());
|
||||
Bindings.setupSentry();
|
||||
// Explicit, ordered service list: the previous reflective jar scan gave hash-ordered
|
||||
// enable/disable and paid a full-jar class sweep at boot. Infrastructure first,
|
||||
@@ -777,7 +777,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
removeShutdownHook();
|
||||
}
|
||||
if (BukkitPlatform.hasHud()) {
|
||||
BukkitPlatform.hudSlots().shutdown();
|
||||
BukkitPlatform.hudBar().shutdown();
|
||||
BukkitPlatform.hudLanes().shutdown();
|
||||
}
|
||||
if (configHotloadEngine != null) {
|
||||
|
||||
+16
-6
@@ -148,10 +148,15 @@ public class CommandIris implements DirectorExecutor {
|
||||
|
||||
IrisDimension dimension = IrisToolbelt.getDimension(resolvedType);
|
||||
if (dimension == null) {
|
||||
sender().sendMessage("Could not find dimension '" + resolvedType + "'.");
|
||||
sender().sendMessage(IrisLanguage.text(
|
||||
BukkitCommandMessagesExtended.COMMAND_IRIS_DIMENSION_NOT_FOUND,
|
||||
MessageArgument.untrusted("dimension", resolvedType)
|
||||
));
|
||||
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_TRY_ONE_OVERWORLD_VANILLA_FLAT_THEEND));
|
||||
sender().sendMessage("Install its pack with " + PackDownloader.downloadCommandFor(resolvedType)
|
||||
+ " and restart the server.");
|
||||
sender().sendMessage(IrisLanguage.text(
|
||||
BukkitCommandMessagesExtended.COMMAND_IRIS_INSTALL_PACK_AND_RESTART,
|
||||
MessageArgument.untrusted("command", PackDownloader.downloadCommandFor(resolvedType))
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -227,10 +232,15 @@ public class CommandIris implements DirectorExecutor {
|
||||
: type;
|
||||
IrisDimension dimension = IrisToolbelt.getDimension(resolvedType);
|
||||
if (dimension == null) {
|
||||
sender().sendMessage("Could not find dimension '" + resolvedType + "'.");
|
||||
sender().sendMessage(IrisLanguage.text(
|
||||
BukkitCommandMessagesExtended.COMMAND_IRIS_DIMENSION_NOT_FOUND,
|
||||
MessageArgument.untrusted("dimension", resolvedType)
|
||||
));
|
||||
sender().sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_IRIS_TRY_ONE_OVERWORLD_VANILLA_FLAT_THEEND));
|
||||
sender().sendMessage("Install its pack with " + PackDownloader.downloadCommandFor(resolvedType)
|
||||
+ " and restart the server.");
|
||||
sender().sendMessage(IrisLanguage.text(
|
||||
BukkitCommandMessagesExtended.COMMAND_IRIS_INSTALL_PACK_AND_RESTART,
|
||||
MessageArgument.untrusted("command", PackDownloader.downloadCommandFor(resolvedType))
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+6
-37
@@ -77,18 +77,12 @@ import art.arcane.iris.util.common.plugin.VolmitSender;
|
||||
import art.arcane.iris.util.common.scheduling.J;
|
||||
import art.arcane.volmlib.util.scheduling.O;
|
||||
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
|
||||
import art.arcane.volmlib.util.hud.HudPriority;
|
||||
import art.arcane.volmlib.util.hud.HudSlotClaim;
|
||||
import art.arcane.volmlib.util.hud.HudSlotRequest;
|
||||
import art.arcane.volmlib.util.hud.HudSurface;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.FluidCollisionMode;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.boss.BarColor;
|
||||
import org.bukkit.boss.BarStyle;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
@@ -108,7 +102,6 @@ import java.util.Objects;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import art.arcane.iris.core.localization.IrisLanguage;
|
||||
@@ -343,8 +336,7 @@ public class CommandStudio implements DirectorExecutor {
|
||||
// previously leaked the whole sampler ForkJoinPool, the self-rescheduling
|
||||
// progress task and both HUD claims for the rest of the server's uptime.
|
||||
MultiBurst multiBurst = null;
|
||||
HudSlotClaim titleClaim = null;
|
||||
HudSlotClaim barClaim = null;
|
||||
boolean progressShown = false;
|
||||
int c = -1;
|
||||
try {
|
||||
engine.getDimension().getRegions().forEach(key -> data.put(key, new AtomicInteger(0)));
|
||||
@@ -354,31 +346,11 @@ public class CommandStudio implements DirectorExecutor {
|
||||
var loc = player.getLocation();
|
||||
int totalTasks = d * d;
|
||||
AtomicInteger completedTasks = new AtomicInteger(0);
|
||||
titleClaim = BukkitPlatform.hudSlots().open(player, new HudSlotRequest("iris:job", HudPriority.PROGRESS, 1200L, List.of(HudSurface.TITLE)));
|
||||
barClaim = BukkitPlatform.hudSlots().open(player, new HudSlotRequest("iris:job", HudPriority.PROGRESS, 1200L, List.of(HudSurface.ACTION_BAR, HudSurface.BOSS_BAR)));
|
||||
HudSlotClaim finalTitleClaim = titleClaim;
|
||||
HudSlotClaim finalBarClaim = barClaim;
|
||||
AtomicLong lastResolveMs = new AtomicLong(0L);
|
||||
progressShown = true;
|
||||
c = J.ar(() -> {
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastResolveMs.get() >= 250L) {
|
||||
lastResolveMs.set(now);
|
||||
finalTitleClaim.resolve();
|
||||
finalBarClaim.resolve();
|
||||
}
|
||||
double jobProgress = (double) completedTasks.get() / totalTasks;
|
||||
HudSurface barSurface = finalBarClaim.granted();
|
||||
sender.sendProgress(
|
||||
jobProgress,
|
||||
IrisLanguage.text(RuntimeUiMessages.FINDING_REGIONS),
|
||||
finalTitleClaim.granted(),
|
||||
barSurface
|
||||
);
|
||||
if (barSurface == HudSurface.BOSS_BAR) {
|
||||
BukkitPlatform.hudLanes().show(player, "iris:job", IrisLanguage.text(RuntimeUiMessages.FINDING_REGIONS) + " " + Form.pc(jobProgress, 0), jobProgress, BarColor.BLUE, BarStyle.SOLID, 4000L);
|
||||
} else if (barSurface == HudSurface.ACTION_BAR) {
|
||||
BukkitPlatform.hudLanes().hide(player, "iris:job");
|
||||
}
|
||||
sender.sendProgress(jobProgress, IrisLanguage.text(RuntimeUiMessages.FINDING_REGIONS));
|
||||
BukkitPlatform.showProgressLane(player, "iris:job", IrisLanguage.text(RuntimeUiMessages.FINDING_REGIONS) + " " + Form.pc(jobProgress, 0), jobProgress, 4000L);
|
||||
}, 0);
|
||||
new Spiraler(d, d, (x, z) -> executor.queue(() -> {
|
||||
var region = engine.getRegion((x << 4) + 8, (z << 4) + 8);
|
||||
@@ -399,11 +371,8 @@ public class CommandStudio implements DirectorExecutor {
|
||||
if (c != -1) {
|
||||
J.car(c);
|
||||
}
|
||||
if (titleClaim != null) {
|
||||
titleClaim.release();
|
||||
}
|
||||
if (barClaim != null) {
|
||||
barClaim.release();
|
||||
if (progressShown) {
|
||||
sender.sendAction(" ");
|
||||
BukkitPlatform.hudLanes().hide(player, "iris:job");
|
||||
}
|
||||
if (multiBurst != null) {
|
||||
|
||||
+24
@@ -7,6 +7,8 @@ import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -71,4 +73,26 @@ public class CommandIrisCreateOverwriteContractTest {
|
||||
assertEquals(Long.valueOf(Long.MAX_VALUE), handler.parse(Long.toString(Long.MAX_VALUE), false));
|
||||
assertThrows(DirectorParsingException.class, () -> handler.parse("9223372036854775808", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createAndReplaceUseLocalizedStyledMissingDimensionFeedback() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/commands/CommandIris.java"
|
||||
));
|
||||
|
||||
assertEquals(2, occurrences(source, "COMMAND_IRIS_DIMENSION_NOT_FOUND"));
|
||||
assertEquals(2, occurrences(source, "COMMAND_IRIS_INSTALL_PACK_AND_RESTART"));
|
||||
assertFalse(source.contains("sendMessage(\"Could not find dimension"));
|
||||
assertFalse(source.contains("sendMessage(\"Install its pack with"));
|
||||
}
|
||||
|
||||
private static int occurrences(String value, String match) {
|
||||
int count = 0;
|
||||
int offset = 0;
|
||||
while ((offset = value.indexOf(match, offset)) >= 0) {
|
||||
count++;
|
||||
offset += match.length();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
+84
-17
@@ -33,7 +33,9 @@ public final class NativeStructureSurfaceFitter {
|
||||
private static final double SURFACE_TERRAIN_FALLOFF = 2.0;
|
||||
private static final long SURFACE_TERRAIN_INFLUENCE_SCALE = 1_000_000L;
|
||||
private static final int SURFACE_TERRAIN_RADIUS = 12;
|
||||
private static final int MAX_VACUUM_TEMPLATE_CELLS = 4_194_304;
|
||||
private static final int MAX_SURFACE_TEMPLATE_CELLS = 4_194_304;
|
||||
private static final Set<String> WARNED_SOURCE_BUDGET =
|
||||
ConcurrentHashMap.newKeySet();
|
||||
private static final Set<String> WARNED_VACUUM_BUDGET =
|
||||
ConcurrentHashMap.newKeySet();
|
||||
|
||||
@@ -45,23 +47,24 @@ public final class NativeStructureSurfaceFitter {
|
||||
List<NativeStructureTerrainIntegrator.TerrainTarget> targets,
|
||||
IntBinaryOperator surfaceHeight) {
|
||||
return prepareSurfaceStructures(
|
||||
world, area, targets, surfaceHeight, MAX_VACUUM_TEMPLATE_CELLS);
|
||||
world, area, targets, surfaceHeight, MAX_SURFACE_TEMPLATE_CELLS);
|
||||
}
|
||||
|
||||
static VacuumFoundationPlan prepareSurfaceStructures(
|
||||
WorldGenLevel world, BoundingBox area,
|
||||
List<NativeStructureTerrainIntegrator.TerrainTarget> targets,
|
||||
IntBinaryOperator surfaceHeight, int maximumVacuumTemplateCells) {
|
||||
IntBinaryOperator surfaceHeight, int maximumTemplateCells) {
|
||||
if (targets == null || targets.isEmpty()) {
|
||||
return VacuumFoundationPlan.empty();
|
||||
}
|
||||
Objects.requireNonNull(surfaceHeight, "Surface structure terrain fitting requires an Iris height resolver");
|
||||
List<SurfaceAnchor> anchors = collectSourceSurfaceAnchors(targets);
|
||||
List<SurfaceAnchor> anchors = collectSourceSurfaceAnchors(
|
||||
world, area, targets, maximumTemplateCells);
|
||||
if (!anchors.isEmpty()) {
|
||||
fitSurfaceTerrain(world, area, anchors, surfaceHeight);
|
||||
}
|
||||
VacuumFootprint vacuum = collectVacuumFootprint(
|
||||
world, area, targets, maximumVacuumTemplateCells);
|
||||
world, area, targets, maximumTemplateCells);
|
||||
if (!vacuum.anchors().isEmpty()) {
|
||||
fitVacuumTerrain(world, area, vacuum, surfaceHeight);
|
||||
}
|
||||
@@ -223,7 +226,14 @@ public final class NativeStructureSurfaceFitter {
|
||||
}
|
||||
|
||||
private static List<SurfaceAnchor> collectSourceSurfaceAnchors(
|
||||
List<NativeStructureTerrainIntegrator.TerrainTarget> targets) {
|
||||
WorldGenLevel world, BoundingBox area,
|
||||
List<NativeStructureTerrainIntegrator.TerrainTarget> targets,
|
||||
int maximumTemplateCells) {
|
||||
BoundingBox influenceArea = new BoundingBox(
|
||||
area.minX() - SURFACE_TERRAIN_RADIUS, area.minY(),
|
||||
area.minZ() - SURFACE_TERRAIN_RADIUS,
|
||||
area.maxX() + SURFACE_TERRAIN_RADIUS, area.maxY(),
|
||||
area.maxZ() + SURFACE_TERRAIN_RADIUS);
|
||||
List<SurfaceAnchor> anchors = new ArrayList<>();
|
||||
for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) {
|
||||
if (!requiresSourceSurfaceTerrain(target)) {
|
||||
@@ -231,13 +241,34 @@ public final class NativeStructureSurfaceFitter {
|
||||
}
|
||||
StructureStart start = target.start();
|
||||
TerrainAdjustment adjustment = start.getStructure().terrainAdaptation();
|
||||
BoundingBox firstBounds = start.getPieces().getFirst().getBoundingBox();
|
||||
BlockPos firstCenter = firstBounds.getCenter();
|
||||
BlockPos referencePosition = new BlockPos(
|
||||
firstCenter.getX(), firstBounds.minY(), firstCenter.getZ());
|
||||
TemplateCellBudget budget = new TemplateCellBudget(maximumTemplateCells);
|
||||
boolean budgetExceeded = false;
|
||||
for (StructurePiece piece : start.getPieces()) {
|
||||
if (piece instanceof PoolElementStructurePiece poolPiece) {
|
||||
if (poolPiece.getElement().getProjection() == StructureTemplatePool.Projection.RIGID) {
|
||||
StructureTemplatePool.Projection projection =
|
||||
poolPiece.getElement().getProjection();
|
||||
if (projection == StructureTemplatePool.Projection.RIGID) {
|
||||
BoundingBox bounds = poolPiece.getBoundingBox();
|
||||
anchors.add(surfaceAnchor(
|
||||
bounds, bounds.minY() + poolPiece.getGroundLevelDelta(),
|
||||
2, adjustment));
|
||||
} else if (!budgetExceeded
|
||||
&& projection == StructureTemplatePool.Projection.TERRAIN_MATCHING
|
||||
&& intersectsHorizontally(poolPiece.getBoundingBox(), influenceArea)) {
|
||||
try {
|
||||
List<SurfaceAnchor> pieceAnchors = new ArrayList<>();
|
||||
addTerrainMatchingSurfaceAnchors(
|
||||
world, influenceArea, poolPiece, referencePosition,
|
||||
adjustment, budget, pieceAnchors);
|
||||
anchors.addAll(pieceAnchors);
|
||||
} catch (TemplateBudgetExceeded ignored) {
|
||||
budgetExceeded = true;
|
||||
warnSourceBudget(target);
|
||||
}
|
||||
}
|
||||
for (JigsawJunction junction : poolPiece.getJunctions()) {
|
||||
anchors.add(new SurfaceAnchor(
|
||||
@@ -255,6 +286,42 @@ public final class NativeStructureSurfaceFitter {
|
||||
return List.copyOf(anchors);
|
||||
}
|
||||
|
||||
private static void addTerrainMatchingSurfaceAnchors(
|
||||
WorldGenLevel world, BoundingBox influenceArea,
|
||||
PoolElementStructurePiece piece, BlockPos referencePosition,
|
||||
TerrainAdjustment adjustment, TemplateCellBudget budget,
|
||||
List<SurfaceAnchor> anchors) {
|
||||
NativeStructureTemplateOccupancy.OccupancyResult occupancy =
|
||||
NativeStructureTemplateOccupancy.resolve(
|
||||
world, piece, referencePosition, influenceArea,
|
||||
() -> world.getLevel().getStructureManager(),
|
||||
influenceArea::isInside, budget::consume);
|
||||
if (!occupancy.resolved()) {
|
||||
return;
|
||||
}
|
||||
int groundY = piece.getBoundingBox().minY() + piece.getGroundLevelDelta();
|
||||
Map<Long, NativeStructureTemplateOccupancy.LowestCell> lowest =
|
||||
NativeStructureTemplateOccupancy.lowestProcessedSolidCells(occupancy.cells());
|
||||
for (NativeStructureTemplateOccupancy.LowestCell cell : lowest.values()) {
|
||||
budget.consume(1);
|
||||
BoundingBox column = new BoundingBox(
|
||||
cell.x(), groundY, cell.z(), cell.x(), groundY, cell.z());
|
||||
anchors.add(surfaceAnchor(column, groundY, 2, adjustment));
|
||||
}
|
||||
}
|
||||
|
||||
private static void warnSourceBudget(
|
||||
NativeStructureTerrainIntegrator.TerrainTarget target) {
|
||||
String structureId = target.structureId() == null
|
||||
? target.start().getStructure().getClass().getName()
|
||||
: target.structureId();
|
||||
if (WARNED_SOURCE_BUDGET.add(structureId)) {
|
||||
IrisLogging.warn("Native structure SOURCE fitting for '"
|
||||
+ structureId + "' exceeded its bounded template budget; "
|
||||
+ "skipping remaining terrain-matching footprints for this start");
|
||||
}
|
||||
}
|
||||
|
||||
private static VacuumFootprint collectVacuumFootprint(
|
||||
WorldGenLevel world, BoundingBox area,
|
||||
List<NativeStructureTerrainIntegrator.TerrainTarget> targets,
|
||||
@@ -286,7 +353,7 @@ public final class NativeStructureSurfaceFitter {
|
||||
Map<Long, Integer> targetCaps = new HashMap<>();
|
||||
Set<Long> targetFoundationBases = new HashSet<>();
|
||||
Set<Long> targetOccupiedCells = new HashSet<>();
|
||||
VacuumCellBudget budget = new VacuumCellBudget(maximumTemplateCells);
|
||||
TemplateCellBudget budget = new TemplateCellBudget(maximumTemplateCells);
|
||||
try {
|
||||
for (StructurePiece piece : start.getPieces()) {
|
||||
BoundingBox bounds = piece.getBoundingBox();
|
||||
@@ -330,7 +397,7 @@ public final class NativeStructureSurfaceFitter {
|
||||
cell.x(), cell.y(), cell.z()));
|
||||
}
|
||||
}
|
||||
} catch (VacuumBudgetExceeded ignored) {
|
||||
} catch (TemplateBudgetExceeded ignored) {
|
||||
warnVacuumBudget(target);
|
||||
continue;
|
||||
}
|
||||
@@ -837,31 +904,31 @@ public final class NativeStructureSurfaceFitter {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class VacuumCellBudget {
|
||||
private static final class TemplateCellBudget {
|
||||
private final int maximum;
|
||||
private int consumed;
|
||||
|
||||
private VacuumCellBudget(int maximum) {
|
||||
private TemplateCellBudget(int maximum) {
|
||||
if (maximum < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Native structure vacuum cell budget cannot be negative");
|
||||
"Native structure surface template budget cannot be negative");
|
||||
}
|
||||
this.maximum = maximum;
|
||||
}
|
||||
|
||||
private void consume(int amount) {
|
||||
if (amount < 0 || consumed > maximum - amount) {
|
||||
throw VacuumBudgetExceeded.INSTANCE;
|
||||
throw TemplateBudgetExceeded.INSTANCE;
|
||||
}
|
||||
consumed += amount;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class VacuumBudgetExceeded extends RuntimeException {
|
||||
private static final VacuumBudgetExceeded INSTANCE =
|
||||
new VacuumBudgetExceeded();
|
||||
private static final class TemplateBudgetExceeded extends RuntimeException {
|
||||
private static final TemplateBudgetExceeded INSTANCE =
|
||||
new TemplateBudgetExceeded();
|
||||
|
||||
private VacuumBudgetExceeded() {
|
||||
private TemplateBudgetExceeded() {
|
||||
super(null, null, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -89,6 +89,24 @@ final class NativeStructureTemplateOccupancy {
|
||||
return lowest;
|
||||
}
|
||||
|
||||
static Map<Long, LowestCell> lowestProcessedSolidCells(Map<Long, OccupancyCell> occupancy) {
|
||||
Map<Long, LowestCell> lowest = new HashMap<>();
|
||||
for (Map.Entry<Long, OccupancyCell> entry : occupancy.entrySet()) {
|
||||
OccupancyCell cell = entry.getValue();
|
||||
if (cell.blocker() || !isSolidBase(cell.state())) {
|
||||
continue;
|
||||
}
|
||||
BlockPos position = BlockPos.of(entry.getKey());
|
||||
long column = columnKey(position.getX(), position.getZ());
|
||||
LowestCell current = lowest.get(column);
|
||||
if (current == null || position.getY() < current.y()) {
|
||||
lowest.put(column, new LowestCell(
|
||||
position.getX(), position.getY(), position.getZ(), cell));
|
||||
}
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
|
||||
static boolean isSolidBase(BlockState state) {
|
||||
return state != null && state.isSolid()
|
||||
&& !state.is(Blocks.STRUCTURE_VOID)
|
||||
|
||||
+5
-1
@@ -113,7 +113,7 @@ public final class ModdedBlockResolution {
|
||||
"poppy", "dandelion", "oxeye_daisy", "orange_tulip", "pink_tulip", "red_tulip", "white_tulip",
|
||||
"lilac", "dead_bush", "sweet_berry_bush", "rose_bush", "wither_rose", "allium", "blue_orchid",
|
||||
"lily_of_the_valley", "crimson_fungus", "warped_fungus", "red_mushroom", "brown_mushroom",
|
||||
"crimson_roots", "azure_bluet", "weeping_vines", "weeping_vines_plant", "warped_roots",
|
||||
"crimson_roots", "azure_bluet", "cactus", "weeping_vines", "weeping_vines_plant", "warped_roots",
|
||||
"nether_sprouts", "twisting_vines", "twisting_vines_plant", "sugar_cane", "wheat", "potatoes",
|
||||
"carrots", "beetroots", "nether_wart", "sea_pickle", "seagrass", "tall_seagrass",
|
||||
"acacia_button", "birch_button", "crimson_button", "dark_oak_button", "jungle_button",
|
||||
@@ -510,6 +510,10 @@ public final class ModdedBlockResolution {
|
||||
}
|
||||
|
||||
public static boolean canPlaceOnto(Block mat, Block onto) {
|
||||
if (mat == Blocks.CACTUS) {
|
||||
return onto == Blocks.CACTUS || onto == Blocks.SAND || onto == Blocks.RED_SAND;
|
||||
}
|
||||
|
||||
if ((onto == Blocks.CRIMSON_NYLIUM || onto == Blocks.WARPED_NYLIUM)
|
||||
&& (mat == Blocks.CRIMSON_FUNGUS || mat == Blocks.CRIMSON_ROOTS
|
||||
|| mat == Blocks.WARPED_FUNGUS || mat == Blocks.WARPED_ROOTS)) {
|
||||
|
||||
+11
@@ -7,8 +7,10 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* The SPI splits block resolution into a null-returning lookup and an air-falling-back lookup. Modded used to
|
||||
@@ -49,4 +51,13 @@ public class ModdedBlockResolutionContractTest {
|
||||
assertNotNull(state);
|
||||
assertEquals(Blocks.OAK_LOG, state.handle().getBlock());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cactusPlacementRequiresNativeCactusSupport() {
|
||||
assertTrue(ModdedBlockResolution.canPlaceOnto(Blocks.CACTUS, Blocks.CACTUS));
|
||||
assertTrue(ModdedBlockResolution.canPlaceOnto(Blocks.CACTUS, Blocks.SAND));
|
||||
assertTrue(ModdedBlockResolution.canPlaceOnto(Blocks.CACTUS, Blocks.RED_SAND));
|
||||
assertFalse(ModdedBlockResolution.canPlaceOnto(Blocks.CACTUS, Blocks.STONE));
|
||||
assertTrue(ModdedBlockResolution.isDecorant(Blocks.CACTUS.defaultBlockState()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user