From 9ff1c502ef3bc904f09101e7d537d6cbb20c4d05 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sat, 30 May 2026 01:27:53 -0400 Subject: [PATCH] dwa --- core/src/main/java/art/arcane/iris/Iris.java | 38 ++++++++++++-- .../iris/core/commands/CommandIris.java | 4 +- .../java/art/arcane/iris/core/nms/INMS.java | 5 -- .../arcane/iris/core/service/StudioSVC.java | 23 +++++++-- .../arcane/iris/core/tools/IrisCreator.java | 23 +++++++++ .../components/IrisStructureComponent.java | 50 +++++++++++++++++++ .../engine/object/IrisStructurePlacement.java | 6 +++ 7 files changed, 133 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/art/arcane/iris/Iris.java b/core/src/main/java/art/arcane/iris/Iris.java index f6f69f639..74fb92c7e 100644 --- a/core/src/main/java/art/arcane/iris/Iris.java +++ b/core/src/main/java/art/arcane/iris/Iris.java @@ -707,6 +707,15 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware { private void processPendingStartupWorldDeletes() { try { + try { + int unregistered = art.arcane.iris.core.tools.IrisCreator.removeTransientStudioWorldsFromBukkitYml(); + if (unregistered > 0) { + Iris.info("Unregistered " + unregistered + " transient studio world(s) from bukkit.yml on startup."); + } + } catch (Throwable e) { + Iris.reportError("Failed to unregister transient studio worlds from bukkit.yml on startup.", e); + } + LinkedHashMap queue = loadPendingWorldDeleteMap(); for (String transientStudioWorld : TransientWorldCleanupSupport.collectTransientStudioWorldNames(Bukkit.getWorldContainer())) { queue.putIfAbsent(transientStudioWorld.toLowerCase(Locale.ROOT), transientStudioWorld); @@ -722,10 +731,31 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware { continue; } - if (Bukkit.getWorld(worldName) != null) { - Iris.warn("Skipping queued deletion for \"" + worldName + "\" because it is currently loaded."); - remaining.put(worldName.toLowerCase(Locale.ROOT), worldName); - continue; + org.bukkit.World loaded = Bukkit.getWorld(worldName); + if (loaded != null) { + if (TransientWorldCleanupSupport.isTransientStudioWorldName(worldName)) { + try { + PlatformChunkGenerator generator = IrisToolbelt.access(loaded); + if (generator != null) { + generator.close(); + } + IrisToolbelt.evacuate(loaded); + Bukkit.unloadWorld(loaded, false); + Iris.info("Unloaded leftover studio world \"" + worldName + "\" for deletion."); + } catch (Throwable e) { + Iris.reportError("Failed to unload leftover studio world \"" + worldName + "\".", e); + } + + if (Bukkit.getWorld(worldName) != null) { + Iris.warn("Studio world \"" + worldName + "\" is still loaded after unload; will retry next startup."); + remaining.put(worldName.toLowerCase(Locale.ROOT), worldName); + continue; + } + } else { + Iris.warn("Skipping queued deletion for \"" + worldName + "\" because it is currently loaded."); + remaining.put(worldName.toLowerCase(Locale.ROOT), worldName); + continue; + } } boolean foundAny = false; 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 45e579227..3c5e691e2 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 @@ -23,7 +23,6 @@ import art.arcane.iris.core.IrisSettings; import art.arcane.iris.core.IrisWorlds; import art.arcane.iris.core.lifecycle.WorldLifecycleService; import art.arcane.iris.core.loader.IrisData; -import art.arcane.iris.core.nms.INMS; import art.arcane.iris.core.service.StudioSVC; import art.arcane.iris.core.tools.IrisToolbelt; import art.arcane.iris.engine.framework.Engine; @@ -611,8 +610,7 @@ public class CommandIris implements DirectorExecutor { ) { sender().sendMessage(C.GREEN + "Downloading pack: " + pack + "/" + branch + (overwrite ? " overwriting" : "")); if (pack.equals("overworld")) { - String url = "https://github.com/IrisDimensions/overworld/releases/download/" + INMS.OVERWORLD_TAG + "/overworld.zip"; - Iris.service(StudioSVC.class).downloadRelease(sender(), url, overwrite); + Iris.service(StudioSVC.class).downloadBranch(sender(), "IrisDimensions/overworld", "master", overwrite); } else { Iris.service(StudioSVC.class).downloadSearch(sender(), "IrisDimensions/" + pack + "/" + branch, overwrite); } diff --git a/core/src/main/java/art/arcane/iris/core/nms/INMS.java b/core/src/main/java/art/arcane/iris/core/nms/INMS.java index eb4c10eb6..6c22dc0c5 100644 --- a/core/src/main/java/art/arcane/iris/core/nms/INMS.java +++ b/core/src/main/java/art/arcane/iris/core/nms/INMS.java @@ -37,13 +37,8 @@ public class INMS { new Version(21, 11, "v1_21_R7") ); - private static final List PACKS = List.of( - new Version(21, 11, "31100") - ); - //@done private static final INMSBinding binding = bind(); - public static final String OVERWORLD_TAG = getTag(PACKS, "31100"); public static INMSBinding get() { return binding; diff --git a/core/src/main/java/art/arcane/iris/core/service/StudioSVC.java b/core/src/main/java/art/arcane/iris/core/service/StudioSVC.java index 45e22fc21..79c1e5d12 100644 --- a/core/src/main/java/art/arcane/iris/core/service/StudioSVC.java +++ b/core/src/main/java/art/arcane/iris/core/service/StudioSVC.java @@ -24,7 +24,6 @@ import art.arcane.iris.core.IrisSettings; import art.arcane.iris.core.ServerConfigurator; import art.arcane.iris.core.lifecycle.WorldLifecycleService; import art.arcane.iris.core.loader.IrisData; -import art.arcane.iris.core.nms.INMS; import art.arcane.iris.core.pack.IrisPack; import art.arcane.iris.core.pack.PackValidationRegistry; import art.arcane.iris.core.pack.PackValidationResult; @@ -72,9 +71,8 @@ public class StudioSVC implements IrisService { if (!f.exists()) { if (pack.equals("overworld")) { - Iris.info("Downloading Default Pack " + pack); - String url = "https://github.com/IrisDimensions/overworld/releases/download/" + INMS.OVERWORLD_TAG + "/overworld.zip"; - Iris.service(StudioSVC.class).downloadRelease(Iris.getSender(), url, false); + Iris.info("Downloading Default Pack " + pack + " (latest on master)"); + Iris.service(StudioSVC.class).downloadBranch(Iris.getSender(), "IrisDimensions/overworld", "master", false); } else { Iris.warn("Default pack '" + pack + "' is not installed. Please download it manually with /iris download"); } @@ -120,6 +118,13 @@ public class StudioSVC implements IrisService { } activeProject = null; + + try { + art.arcane.iris.core.tools.IrisCreator.removeTransientStudioWorldsFromBukkitYml(); + } catch (Throwable e) { + Iris.reportError("Failed to unregister transient studio worlds from bukkit.yml during shutdown.", e); + } + queueStudioWorldDeletionOnStartup(worldNamesToDelete); } @@ -237,6 +242,16 @@ public class StudioSVC implements IrisService { } } + public void downloadBranch(VolmitSender sender, String repo, String branch, boolean forceOverwrite) { + try { + download(sender, repo, branch, forceOverwrite, false); + } catch (Throwable e) { + Iris.reportError(e); + e.printStackTrace(); + sender.sendMessage("Failed to download '" + repo + "' (branch " + branch + ")."); + } + } + public void download(VolmitSender sender, String repo, String branch) throws JsonSyntaxException, IOException { download(sender, repo, branch, false, false); } diff --git a/core/src/main/java/art/arcane/iris/core/tools/IrisCreator.java b/core/src/main/java/art/arcane/iris/core/tools/IrisCreator.java index 605d76af4..522d63b91 100644 --- a/core/src/main/java/art/arcane/iris/core/tools/IrisCreator.java +++ b/core/src/main/java/art/arcane/iris/core/tools/IrisCreator.java @@ -18,6 +18,7 @@ package art.arcane.iris.core.tools; +import art.arcane.iris.core.runtime.TransientWorldCleanupSupport; import com.google.common.util.concurrent.AtomicDouble; import art.arcane.iris.Iris; import art.arcane.iris.core.IrisRuntimeSchedulerMode; @@ -125,6 +126,28 @@ public class IrisCreator { yml.save(BUKKIT_YML); return true; } + + public static int removeTransientStudioWorldsFromBukkitYml() throws IOException { + YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML); + ConfigurationSection section = yml.getConfigurationSection("worlds"); + if (section == null) { + return 0; + } + int removed = 0; + for (String name : new java.util.ArrayList<>(section.getKeys(false))) { + if (TransientWorldCleanupSupport.isTransientStudioWorldName(name)) { + section.set(name, null); + removed++; + } + } + if (removed > 0) { + if (section.getKeys(false).isEmpty()) { + yml.set("worlds", null); + } + yml.save(BUKKIT_YML); + } + return removed; + } public static boolean worldLoaded(){ return true; } diff --git a/core/src/main/java/art/arcane/iris/engine/mantle/components/IrisStructureComponent.java b/core/src/main/java/art/arcane/iris/engine/mantle/components/IrisStructureComponent.java index df89ec119..c8ee6ed38 100644 --- a/core/src/main/java/art/arcane/iris/engine/mantle/components/IrisStructureComponent.java +++ b/core/src/main/java/art/arcane/iris/engine/mantle/components/IrisStructureComponent.java @@ -18,6 +18,7 @@ package art.arcane.iris.engine.mantle.components; +import art.arcane.iris.Iris; import art.arcane.iris.engine.IrisComplex; import art.arcane.iris.engine.data.cache.Cache; import art.arcane.iris.engine.framework.PlacedStructurePiece; @@ -44,6 +45,8 @@ import art.arcane.volmlib.util.math.RNG; @ComponentFlag(ReservedFlag.JIGSAW) public class IrisStructureComponent extends IrisMantleComponent { + private static final long MAX_BORE_VOLUME = 6_000_000L; + public IrisStructureComponent(EngineMantle engineMantle) { super(engineMantle, ReservedFlag.JIGSAW, 1); } @@ -116,6 +119,10 @@ public class IrisStructureComponent extends IrisMantleComponent { return; } + if (placement.isBore()) { + boreStructure(writer, pieces, placement.getBorePadding()); + } + ObjectPlaceMode mode = structure.getPlaceMode(); if (placement.isUnderground() || mode == ObjectPlaceMode.STRUCTURE_PIECE || mode == ObjectPlaceMode.FLOATING) { for (PlacedStructurePiece p : pieces) { @@ -135,6 +142,49 @@ public class IrisStructureComponent extends IrisMantleComponent { } } + private void boreStructure(MantleWriter writer, KList pieces, int padding) { + int minX = Integer.MAX_VALUE; + int minY = Integer.MAX_VALUE; + int minZ = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int maxY = Integer.MIN_VALUE; + int maxZ = Integer.MIN_VALUE; + for (PlacedStructurePiece p : pieces) { + minX = Math.min(minX, p.getMinX()); + minY = Math.min(minY, p.getMinY()); + minZ = Math.min(minZ, p.getMinZ()); + maxX = Math.max(maxX, p.getMaxX()); + maxY = Math.max(maxY, p.getMaxY()); + maxZ = Math.max(maxZ, p.getMaxZ()); + } + int pad = Math.max(0, padding); + minX -= pad; + minZ -= pad; + maxX += pad; + maxZ += pad; + maxY += pad; + int worldMin = getEngineMantle().getEngine().getMinHeight() + 1; + int worldMax = getEngineMantle().getEngine().getMinHeight() + getEngineMantle().getEngine().getHeight() - 1; + minY = Math.max(minY, worldMin); + maxY = Math.min(maxY, worldMax); + if (maxX < minX || maxY < minY || maxZ < minZ) { + return; + } + long volume = (long) (maxX - minX + 1) * (long) (maxY - minY + 1) * (long) (maxZ - minZ + 1); + if (volume > MAX_BORE_VOLUME) { + Iris.warn("Skipping structure bore of " + volume + " blocks (cap " + MAX_BORE_VOLUME + "); use a smaller structure or larger spacing."); + return; + } + org.bukkit.block.data.BlockData air = org.bukkit.Material.AIR.createBlockData(); + for (int bx = minX; bx <= maxX; bx++) { + for (int by = minY; by <= maxY; by++) { + for (int bz = minZ; bz <= maxZ; bz++) { + writer.set(bx, by, bz, air); + } + } + } + } + private void placeObject(MantleWriter writer, IrisStructure structure, PlacedStructurePiece p, ObjectPlaceMode mode, int y, RNG rng) { IrisObject object = p.getObject(); IrisObjectPlacement config = new IrisObjectPlacement(); diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisStructurePlacement.java b/core/src/main/java/art/arcane/iris/engine/object/IrisStructurePlacement.java index 4cb4275b7..ee6ccf223 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisStructurePlacement.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisStructurePlacement.java @@ -99,6 +99,12 @@ public class IrisStructurePlacement { @Desc("IRIS_PLACED only: if true the structure is placed underground at a random world Y inside [minHeight, maxHeight] (raw stamp, no terrain matching) instead of being dropped onto the terrain surface. Use this for deep structures like ancient cities in a deep cave band.") private boolean underground = false; + @Desc("IRIS_PLACED only: if true, the structure's full bounding box (floor up to roof) is bored out to air before the pieces are stamped, so the structure sits inside an open cavern instead of being encased in solid terrain. Essential for underground structures such as ancient cities to be visible and enterable.") + private boolean bore = false; + + @Desc("IRIS_PLACED only: extra blocks of air clearance added around the bored bounding box (horizontally and above) when bore=true. The floor is never bored below the structure so support is preserved.") + private int borePadding = 0; + @Desc("If false, this placement is skipped underwater.") private boolean underwater = false; }