From 23fad24fb7c901956f7ecb63affda3e445cf2a3d Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Wed, 22 Apr 2026 16:33:25 -0400 Subject: [PATCH] Whoops --- .../iris/core/commands/CommandFind.java | 119 ++++++++++++++++++ .../iris/core/commands/CommandIris.java | 1 + .../mantle/components/IslandObjectPlacer.java | 76 +++++++---- .../MantleFloatingObjectComponent.java | 55 ++++---- .../object/FloatingObjectFootprint.java | 26 ++-- 5 files changed, 207 insertions(+), 70 deletions(-) create mode 100644 core/src/main/java/art/arcane/iris/core/commands/CommandFind.java diff --git a/core/src/main/java/art/arcane/iris/core/commands/CommandFind.java b/core/src/main/java/art/arcane/iris/core/commands/CommandFind.java new file mode 100644 index 000000000..5c06e4489 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/core/commands/CommandFind.java @@ -0,0 +1,119 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package art.arcane.iris.core.commands; + +import art.arcane.iris.Iris; +import art.arcane.iris.core.service.ObjectStudioSaveService; +import art.arcane.iris.engine.framework.Engine; +import art.arcane.iris.engine.object.IrisBiome; +import art.arcane.iris.engine.object.IrisRegion; +import art.arcane.iris.util.common.director.DirectorExecutor; +import art.arcane.iris.util.common.director.specialhandlers.ObjectHandler; +import art.arcane.iris.util.common.format.C; +import art.arcane.volmlib.util.director.DirectorOrigin; +import art.arcane.volmlib.util.director.annotations.Director; +import art.arcane.volmlib.util.director.annotations.Param; +import org.bukkit.entity.Player; + +@Director(name = "find", origin = DirectorOrigin.PLAYER, description = "Iris Find commands", aliases = "goto") +public class CommandFind implements DirectorExecutor { + @Director(description = "Find a biome") + public void biome( + @Param(description = "The biome to look for") + IrisBiome biome, + @Param(description = "Should you be teleported", defaultValue = "true") + boolean teleport + ) { + Engine e = engine(); + + if (e == null) { + sender().sendMessage(C.GOLD + "Not in an Iris World!"); + return; + } + + e.gotoBiome(biome, player(), teleport); + } + + @Director(description = "Find a region") + public void region( + @Param(description = "The region to look for") + IrisRegion region, + @Param(description = "Should you be teleported", defaultValue = "true") + boolean teleport + ) { + Engine e = engine(); + + if (e == null) { + sender().sendMessage(C.GOLD + "Not in an Iris World!"); + return; + } + + e.gotoRegion(region, player(), teleport); + } + + @Director(description = "Find a point of interest.") + public void poi( + @Param(description = "The type of PoI to look for.") + String type, + @Param(description = "Should you be teleported", defaultValue = "true") + boolean teleport + ) { + Engine e = engine(); + if (e == null) { + sender().sendMessage(C.GOLD + "Not in an Iris World!"); + return; + } + + e.gotoPOI(type, player(), teleport); + } + + @Director(description = "Find an object") + public void object( + @Param(description = "The object to look for", customHandler = ObjectHandler.class) + String object, + @Param(description = "Should you be teleported", defaultValue = "true") + boolean teleport + ) { + Engine e = engine(); + + if (e == null) { + sender().sendMessage(C.GOLD + "Not in an Iris World!"); + return; + } + + Player studioPlayer = player(); + if (studioPlayer != null) { + try { + if (ObjectStudioSaveService.get().teleportTo(studioPlayer, object)) { + sender().sendMessage(C.GREEN + "Object Studio: teleporting to " + object); + return; + } + } catch (Throwable t) { + Iris.reportError(t); + } + } + + if (e.hasObjectPlacement(object)) { + e.gotoObject(object, player(), teleport); + return; + } + + sender().sendMessage(C.RED + object + " is not configured in any region/biome object placements."); + } +} diff --git a/core/src/main/java/art/arcane/iris/core/commands/CommandIris.java b/core/src/main/java/art/arcane/iris/core/commands/CommandIris.java index 7413fa0c2..5b604486c 100644 --- a/core/src/main/java/art/arcane/iris/core/commands/CommandIris.java +++ b/core/src/main/java/art/arcane/iris/core/commands/CommandIris.java @@ -101,6 +101,7 @@ public class CommandIris implements DirectorExecutor { private CommandEdit edit; private CommandDeveloper developer; private CommandPack pack; + private CommandFind find; public static boolean worldCreation = false; private static final AtomicReference mainWorld = new AtomicReference<>(); String WorldEngine; diff --git a/core/src/main/java/art/arcane/iris/engine/mantle/components/IslandObjectPlacer.java b/core/src/main/java/art/arcane/iris/engine/mantle/components/IslandObjectPlacer.java index 159af8ad1..5f271dfd3 100644 --- a/core/src/main/java/art/arcane/iris/engine/mantle/components/IslandObjectPlacer.java +++ b/core/src/main/java/art/arcane/iris/engine/mantle/components/IslandObjectPlacer.java @@ -33,6 +33,7 @@ public class IslandObjectPlacer implements IObjectPlacer { private final MantleWriter wrapped; private final FloatingIslandSample[] samples; + private final boolean[] overhangAllowed; private final int minX; private final int minZ; private final int chunkMaxIslandTopY; @@ -57,6 +58,38 @@ public class IslandObjectPlacer implements IObjectPlacer { } } this.chunkMaxIslandTopY = maxY; + this.overhangAllowed = buildOverhangMask(samples); + } + + private static boolean[] buildOverhangMask(FloatingIslandSample[] samples) { + boolean[] mask = new boolean[256]; + for (int zf = 0; zf < 16; zf++) { + for (int xf = 0; xf < 16; xf++) { + int idx = (zf << 4) | xf; + if (samples[idx] != null) { + mask[idx] = true; + continue; + } + boolean touchedEdge = false; + boolean found = false; + for (int dz = -OVERHANG_RADIUS; dz <= OVERHANG_RADIUS && !found; dz++) { + int nzf = zf + dz; + for (int dx = -OVERHANG_RADIUS; dx <= OVERHANG_RADIUS; dx++) { + int nxf = xf + dx; + if (nxf < 0 || nxf >= 16 || nzf < 0 || nzf >= 16) { + touchedEdge = true; + continue; + } + if (samples[(nzf << 4) | nxf] != null) { + found = true; + break; + } + } + } + mask[idx] = found || touchedEdge; + } + } + return mask; } public int getWritesAttempted() { @@ -73,38 +106,29 @@ public class IslandObjectPlacer implements IObjectPlacer { private boolean shouldSkipAirColumn(int x, int y, int z) { writesAttempted++; - if (sampleAt(x, z) != null) { + int xf = x - minX; + int zf = z - minZ; + if (xf >= 0 && xf < 16 && zf >= 0 && zf < 16) { + int idx = (zf << 4) | xf; + if (samples[idx] != null) { + return false; + } + if (y <= anchorTopY) { + writesDroppedBelow++; + return true; + } + if (!overhangAllowed[idx]) { + writesDroppedOverhang++; + return true; + } return false; } if (y <= anchorTopY) { writesDroppedBelow++; return true; } - if (!hasIslandNeighborWithin(x, z, OVERHANG_RADIUS)) { - writesDroppedOverhang++; - return true; - } - return false; - } - - private boolean hasIslandNeighborWithin(int x, int z, int radius) { - int xf = x - minX; - int zf = z - minZ; - boolean touchedChunkEdge = false; - for (int dx = -radius; dx <= radius; dx++) { - int nxf = xf + dx; - for (int dz = -radius; dz <= radius; dz++) { - int nzf = zf + dz; - if (nxf < 0 || nxf >= 16 || nzf < 0 || nzf >= 16) { - touchedChunkEdge = true; - continue; - } - if (samples[(nzf << 4) | nxf] != null) { - return true; - } - } - } - return touchedChunkEdge; + writesDroppedOverhang++; + return true; } private @Nullable FloatingIslandSample sampleAt(int x, int z) { diff --git a/core/src/main/java/art/arcane/iris/engine/mantle/components/MantleFloatingObjectComponent.java b/core/src/main/java/art/arcane/iris/engine/mantle/components/MantleFloatingObjectComponent.java index 50fe179e1..965701b84 100644 --- a/core/src/main/java/art/arcane/iris/engine/mantle/components/MantleFloatingObjectComponent.java +++ b/core/src/main/java/art/arcane/iris/engine/mantle/components/MantleFloatingObjectComponent.java @@ -62,6 +62,7 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent { private static final int HEAVY_CLIP_WARNING_CAP = 30; private static final double HEAVY_CLIP_RATIO = 0.5; private static final int MIN_FOOTPRINT_CELLS_CHECKED = 3; + private static final IrisObjectRotation ROTATION_NONE = IrisObjectRotation.of(0, 0, 0); public static final java.util.concurrent.ConcurrentHashMap anchorYHisto = new java.util.concurrent.ConcurrentHashMap<>(); public MantleFloatingObjectComponent(EngineMantle engineMantle) { @@ -106,23 +107,15 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent { } } - private static void verifyTerrainBelowObject(MantleWriter writer, IrisObject obj, int wx, int wz, int pickTopY, FloatingIslandSample sample) { - long warned = terrainMismatchWarnings.get(); - if (warned >= TERRAIN_MISMATCH_WARNING_CAP) { + private static void verifyTerrainBelowObject(IrisObject obj, int wx, int wz, int pickTopY, FloatingIslandSample sample) { + if (terrainMismatchWarnings.get() >= TERRAIN_MISMATCH_WARNING_CAP) { return; } - boolean mantleSolid; - try { - mantleSolid = writer.isSolid(wx, pickTopY, wz); - } catch (Throwable t) { - mantleSolid = false; - } - boolean sampleSolid = sample != null + if (sample != null && sample.solidMask != null && sample.topIdx >= 0 && sample.topIdx < sample.solidMask.length - && sample.solidMask[sample.topIdx]; - if (mantleSolid && sampleSolid) { + && sample.solidMask[sample.topIdx]) { return; } if (terrainMismatchWarnings.incrementAndGet() > TERRAIN_MISMATCH_WARNING_CAP) { @@ -135,9 +128,7 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent { String sampleMaskLen = sample == null || sample.solidMask == null ? "null" : String.valueOf(sample.solidMask.length); Iris.warn("[FloatingTerrainCheck] object=" + objKey + " at=(" + wx + "," + (pickTopY + 1) + "," + wz + ")" - + " expected solid below at y=" + pickTopY - + " mantleSolid=" + mantleSolid - + " sampleSolid=" + sampleSolid + + " sample reports non-solid trunk column" + " sampleTopY=" + sampleTop + " sampleBaseY=" + sampleBase + " sampleTopIdx=" + sampleTopIdx @@ -198,16 +189,21 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent { } } - if (entry.isInheritObjects() && target != null) { - for (IrisObjectPlacement placement : target.getSurfaceObjects()) { - tryPlaceAnchoredChunk(writer, complex, chunkRng, data, placement, samples, columns, minX, minZ, entry, target); - } - } - + KList surface = entry.isInheritObjects() && target != null ? target.getSurfaceObjects() : null; KList extras = entry.getExtraObjects(); - if (extras != null && !extras.isEmpty()) { - for (IrisObjectPlacement placement : extras) { - tryPlaceAnchoredChunk(writer, complex, chunkRng, data, placement, samples, columns, minX, minZ, entry, target); + boolean hasSurface = surface != null && !surface.isEmpty(); + boolean hasExtras = extras != null && !extras.isEmpty(); + if (hasSurface || hasExtras) { + KList interior = interiorColumns(samples, columns); + if (hasSurface) { + for (IrisObjectPlacement placement : surface) { + tryPlaceAnchoredChunk(writer, complex, chunkRng, data, placement, samples, columns, interior, minX, minZ, entry); + } + } + if (hasExtras) { + for (IrisObjectPlacement placement : extras) { + tryPlaceAnchoredChunk(writer, complex, chunkRng, data, placement, samples, columns, interior, minX, minZ, entry); + } } } } @@ -265,13 +261,12 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent { } @ChunkCoordinates - private void tryPlaceAnchoredChunk(MantleWriter writer, IrisComplex complex, RNG rng, IrisData data, IrisObjectPlacement placement, FloatingIslandSample[] samples, KList columns, int minX, int minZ, IrisFloatingChildBiomes entry, IrisBiome target) { + private void tryPlaceAnchoredChunk(MantleWriter writer, IrisComplex complex, RNG rng, IrisData data, IrisObjectPlacement placement, FloatingIslandSample[] samples, KList columns, KList interior, int minX, int minZ, IrisFloatingChildBiomes entry) { if (placement == null || columns.isEmpty()) { return; } int density = placement.getDensity(rng, minX, minZ, data); double perAttempt = placement.getChance(); - KList interior = interiorColumns(samples, columns); for (int i = 0; i < density; i++) { objectsAttempted.incrementAndGet(); @@ -329,7 +324,7 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent { IrisObjectPlacement anchored = placement.toPlacement(obj.getLoadKey()); anchored.setMode(translateStiltModeForFloating(anchored.getMode())); anchored.setTranslate(new IrisObjectTranslate()); - anchored.setRotation(IrisObjectRotation.of(0, 0, 0)); + anchored.setRotation(ROTATION_NONE); anchored.setForcePlace(true); anchored.setBottom(false); @@ -349,7 +344,7 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent { recordAnchorYHisto(pickTopY); int trunkWx = minX + pickedXf; int trunkWz = minZ + pickedZf; - verifyTerrainBelowObject(writer, obj, trunkWx, trunkWz, pickTopY, pickedSample); + verifyTerrainBelowObject(obj, trunkWx, trunkWz, pickTopY, pickedSample); recordWriteStats(obj, trunkWx, trunkWz, pickTopY, islandPlacer); } catch (Throwable e) { Iris.reportError(e); @@ -362,7 +357,9 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent { int tallestKz = fp.getTallestKz(); int checked = 0; boolean touchedChunkEdge = false; - for (long encoded : fp.footprintXZ()) { + long[] cells = fp.footprintXZ(); + for (int i = 0, n = cells.length; i < n; i++) { + long encoded = cells[i]; int kx = (int) (encoded >> 32); int kz = (int) (encoded & 0xFFFFFFFFL); int colXf = pickedXf + (kx - tallestKx); diff --git a/core/src/main/java/art/arcane/iris/engine/object/FloatingObjectFootprint.java b/core/src/main/java/art/arcane/iris/engine/object/FloatingObjectFootprint.java index ed9ea46ef..c8697ae9d 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/FloatingObjectFootprint.java +++ b/core/src/main/java/art/arcane/iris/engine/object/FloatingObjectFootprint.java @@ -24,13 +24,9 @@ import org.bukkit.block.data.BlockData; import org.bukkit.util.BlockVector; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; @@ -44,16 +40,16 @@ public class FloatingObjectFootprint { private final int centerZ; private final int tallestKx; private final int tallestKz; - private final Set footprintXZ; + private final long[] footprintXZ; - private FloatingObjectFootprint(int lowestSolidKeyY, int centerX, int centerY, int centerZ, int tallestKx, int tallestKz, Set footprintXZ) { + private FloatingObjectFootprint(int lowestSolidKeyY, int centerX, int centerY, int centerZ, int tallestKx, int tallestKz, long[] footprintXZ) { this.lowestSolidKeyY = lowestSolidKeyY; this.centerX = centerX; this.centerY = centerY; this.centerZ = centerZ; this.tallestKx = tallestKx; this.tallestKz = tallestKz; - this.footprintXZ = Collections.unmodifiableSet(footprintXZ); + this.footprintXZ = footprintXZ; } public static FloatingObjectFootprint compute(IrisObject obj) { @@ -65,7 +61,6 @@ public class FloatingObjectFootprint { int cx = obj.getCenter().getBlockX(); int cy = obj.getCenter().getBlockY(); int cz = obj.getCenter().getBlockZ(); - Set xzSet = new HashSet<>(); Map columnStats = new HashMap<>(); obj.getBlocks().forEach((BlockVector key, BlockData bd) -> { @@ -76,7 +71,6 @@ public class FloatingObjectFootprint { int ky = key.getBlockY(); int kz = key.getBlockZ(); long packed = ((long) kx << 32) | (kz & 0xFFFFFFFFL); - xzSet.add(packed); int[] stats = columnStats.get(packed); if (stats == null) { stats = new int[]{ky, 1}; @@ -89,6 +83,12 @@ public class FloatingObjectFootprint { } }); + long[] footprintArray = new long[columnStats.size()]; + int idx = 0; + for (Long packed : columnStats.keySet()) { + footprintArray[idx++] = packed; + } + long tallestPacked = resolveTallestColumn(columnStats); int lowestSolidKeyY = columnStats.isEmpty() ? cy @@ -98,7 +98,7 @@ public class FloatingObjectFootprint { if (DIAGNOSTIC_LOG) { logFootprintDiagnostic(cacheKey, obj, cx, cy, cz, lowestSolidKeyY, tallestKx, tallestKz, columnStats); } - return new FloatingObjectFootprint(lowestSolidKeyY, cx, cy, cz, tallestKx, tallestKz, xzSet); + return new FloatingObjectFootprint(lowestSolidKeyY, cx, cy, cz, tallestKx, tallestKz, footprintArray); } private static void logFootprintDiagnostic(String cacheKey, IrisObject obj, int cx, int cy, int cz, int anchorY, int tallestKx, int tallestKz, Map columnStats) { @@ -204,10 +204,6 @@ public class FloatingObjectFootprint { return bestPacked; } - public boolean columnInFootprint(int objKeyX, int objKeyZ) { - return footprintXZ.contains(((long) objKeyX << 32) | (objKeyZ & 0xFFFFFFFFL)); - } - public int lowestSolidRelCenterY() { return lowestSolidKeyY - centerY; } @@ -236,7 +232,7 @@ public class FloatingObjectFootprint { return tallestKz; } - public Set footprintXZ() { + public long[] footprintXZ() { return footprintXZ; } }