From fb0bc112e366294309a0850bf2a5a39fef360298 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 30 Mar 2025 14:31:11 +0200 Subject: [PATCH 001/138] increase worker threads on paper servers during pregen --- .../com/volmit/iris/core/IrisSettings.java | 6 ++- .../methods/AsyncPregenMethod.java | 45 +++++++++++++++++++ .../iris/core/tools/IrisPackBenchmarking.java | 41 ++--------------- 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 5381a7e39..eb1cb1d11 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -24,7 +24,6 @@ import com.volmit.iris.util.io.IO; import com.volmit.iris.util.json.JSONException; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.plugin.VolmitSender; -import com.volmit.iris.util.scheduling.ChronoLatch; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -135,6 +134,11 @@ public class IrisSettings { @Data public static class IrisSettingsConcurrency { public int parallelism = -1; + public int worldGenParallelism = -1; + + public int getWorldGenThreads() { + return getThreadCount(worldGenParallelism); + } } @Data diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java index 8496f924c..16c2fd8c1 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java @@ -19,6 +19,7 @@ package com.volmit.iris.core.pregenerator.methods; import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.pregenerator.PregenListener; import com.volmit.iris.core.pregenerator.PregeneratorMethod; import com.volmit.iris.core.tools.IrisToolbelt; @@ -34,8 +35,10 @@ import org.bukkit.World; import java.util.ArrayList; import java.util.Map; import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicInteger; public class AsyncPregenMethod implements PregeneratorMethod { + private static final AtomicInteger THREAD_COUNT = new AtomicInteger(); private final World world; private final MultiBurst burst; private final Semaphore semaphore; @@ -92,6 +95,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { @Override public void init() { unloadAndSaveAllChunks(); + increaseWorkerThreads(); } @Override @@ -104,6 +108,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { semaphore.acquireUninterruptibly(256); unloadAndSaveAllChunks(); burst.close(); + resetWorkerThreads(); } @Override @@ -140,4 +145,44 @@ public class AsyncPregenMethod implements PregeneratorMethod { return null; } + + + public static void increaseWorkerThreads() { + THREAD_COUNT.updateAndGet(i -> { + if (i > 0) return 1; + try { + var field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL"); + var pool = field.get(null); + var threads = ((Thread[]) pool.getClass().getDeclaredMethod("getCoreThreads").invoke(pool)).length; + var adjusted = IrisSettings.get().getConcurrency().getWorldGenThreads(); + if (threads >= adjusted) return 0; + + pool.getClass().getDeclaredMethod("adjustThreadCount", int.class).invoke(pool, adjusted); + return threads; + } catch (ClassNotFoundException ignored) { + } catch (Throwable e) { + Iris.error("Failed to increase worker threads"); + e.printStackTrace(); + } + return 0; + }); + } + + public static void resetWorkerThreads() { + THREAD_COUNT.updateAndGet(i -> { + if (i == 0) return 0; + try { + var field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL"); + var pool = field.get(null); + var method = pool.getClass().getDeclaredMethod("adjustThreadCount", int.class); + method.invoke(pool, i); + return 0; + } catch (ClassNotFoundException ignored) { + } catch (Throwable e) { + Iris.error("Failed to reset worker threads"); + e.printStackTrace(); + } + return i; + }); + } } diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisPackBenchmarking.java b/core/src/main/java/com/volmit/iris/core/tools/IrisPackBenchmarking.java index 8667542a9..d9c6b0bd0 100644 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisPackBenchmarking.java +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisPackBenchmarking.java @@ -9,6 +9,7 @@ import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.exceptions.IrisException; import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.io.IO; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.PrecisionStopwatch; import lombok.Getter; @@ -17,11 +18,6 @@ import org.bukkit.Bukkit; import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; import java.time.Clock; import java.time.LocalDateTime; import java.util.Collections; @@ -50,10 +46,7 @@ public class IrisPackBenchmarking { .start(() -> { Iris.info("Setting up benchmark environment "); benchmarkInProgress = true; - File file = new File("benchmark"); - if (file.exists()) { - deleteDirectory(file.toPath()); - } + IO.delete(new File(Bukkit.getWorldContainer(), "benchmark")); createBenchmark(); while (!IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) { J.sleep(1000); @@ -72,7 +65,7 @@ public class IrisPackBenchmarking { public void finishedBenchmark(KList cps) { try { - String time = Form.duration(stopwatch.getMillis()); + String time = Form.duration((long) stopwatch.getMilliseconds()); Engine engine = IrisToolbelt.access(Bukkit.getWorld("benchmark")).getEngine(); Iris.info("-----------------"); Iris.info("Results:"); @@ -83,11 +76,7 @@ public class IrisPackBenchmarking { Iris.info(" - Lowest CPS: " + findLowest(cps)); Iris.info("-----------------"); Iris.info("Creating a report.."); - File profilers = new File("plugins" + File.separator + "Iris" + File.separator + "packbenchmarks"); - profilers.mkdir(); - - File results = new File(profilers, dimension.getName() + " " + LocalDateTime.now(Clock.systemDefaultZone()).toString().replace(':', '-') + ".txt"); - results.getParentFile().mkdirs(); + File results = Iris.instance.getDataFile("packbenchmarks", dimension.getName() + " " + LocalDateTime.now(Clock.systemDefaultZone()).toString().replace(':', '-') + ".txt"); KMap metrics = engine.getMetrics().pull(); try (FileWriter writer = new FileWriter(results)) { writer.write("-----------------\n"); @@ -178,26 +167,4 @@ public class IrisPackBenchmarking { private int findHighest(KList list) { return Collections.max(list); } - - private boolean deleteDirectory(Path dir) { - try { - Files.walkFileTree(dir, new SimpleFileVisitor<>() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Files.delete(file); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - Files.delete(dir); - return FileVisitResult.CONTINUE; - } - }); - return true; - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } } \ No newline at end of file From 74e2576ca2761db11f8479de4afc22bb381165b5 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 6 Apr 2025 16:31:35 +0200 Subject: [PATCH 002/138] Prevent saving the iris level stems --- core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java | 2 ++ .../main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java | 5 +++++ .../volmit/iris/engine/platform/BukkitChunkGenerator.java | 1 + .../java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java | 5 +++++ .../java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java | 5 +++++ .../java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java | 5 +++++ .../java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java | 5 +++++ .../java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java | 5 +++++ .../java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java | 5 +++++ .../java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java | 5 +++++ 10 files changed, 43 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java index 25e5f688b..d7a28f822 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java @@ -135,4 +135,6 @@ public interface INMSBinding { Pair injectUncached(boolean overworld, boolean nether, boolean end); boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end); + + void removeCustomDimensions(World world); } diff --git a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java index 33f83ddd4..ec0626403 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java +++ b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java @@ -135,6 +135,11 @@ public class NMSBinding1X implements INMSBinding { return false; } + @Override + public void removeCustomDimensions(World world) { + + } + @Override public CompoundTag serializeEntity(Entity location) { return null; diff --git a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index 863d7d268..9b57072c2 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -127,6 +127,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun try { if (initialized || !world.name().equals(event.getWorld().getName())) return; + INMS.get().removeCustomDimensions(event.getWorld()); world.setRawWorldSeed(event.getWorld().getSeed()); Engine engine = getEngine(event.getWorld()); if (engine == null) { diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java index 66723b82a..2e79fabf0 100644 --- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java @@ -687,6 +687,11 @@ public class NMSBinding implements INMSBinding { return overworld || nether || end; } + @Override + public void removeCustomDimensions(World world) { + ((CraftWorld) world).getHandle().K.customDimensions = null; + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(), diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java index 6ef8bd4a8..b587280d4 100644 --- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java @@ -688,6 +688,11 @@ public class NMSBinding implements INMSBinding { return overworld || nether || end; } + @Override + public void removeCustomDimensions(World world) { + ((CraftWorld) world).getHandle().K.customDimensions = null; + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(), diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java index 84eec1ff5..63efb9984 100644 --- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java @@ -689,6 +689,11 @@ public class NMSBinding implements INMSBinding { return overworld || nether || end; } + @Override + public void removeCustomDimensions(World world) { + ((CraftWorld) world).getHandle().K.customDimensions = null; + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(), diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java index 0dffeb195..db25b3bc7 100644 --- a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java @@ -714,6 +714,11 @@ public class NMSBinding implements INMSBinding { return overworld || nether || end; } + @Override + public void removeCustomDimensions(World world) { + ((CraftWorld) world).getHandle().K.customDimensions = null; + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(), diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java index 71b59e65a..09380f1d4 100644 --- a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java +++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java @@ -718,6 +718,11 @@ public class NMSBinding implements INMSBinding { return overworld || nether || end; } + @Override + public void removeCustomDimensions(World world) { + ((CraftWorld) world).getHandle().K.customDimensions = null; + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(), diff --git a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java index 27a379228..0447a4b11 100644 --- a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java +++ b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java @@ -708,6 +708,11 @@ public class NMSBinding implements INMSBinding { return overworld || nether || end; } + @Override + public void removeCustomDimensions(World world) { + ((CraftWorld) world).getHandle().L.customDimensions = null; + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(), diff --git a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java index efb282d86..80a3fdc69 100644 --- a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java +++ b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java @@ -707,6 +707,11 @@ public class NMSBinding implements INMSBinding { return overworld || nether || end; } + @Override + public void removeCustomDimensions(World world) { + ((CraftWorld) world).getHandle().L.customDimensions = null; + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(), From 566fca2b52604b2166009b5e6ac79bdd71b0ed39 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 7 Apr 2025 19:00:06 +0200 Subject: [PATCH 003/138] Add info message for failed increase of worker threads --- .../core/pregenerator/methods/AsyncPregenMethod.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java index 16c2fd8c1..7abeb2256 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java @@ -32,6 +32,7 @@ import io.papermc.lib.PaperLib; import org.bukkit.Chunk; import org.bukkit.World; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Map; import java.util.concurrent.Semaphore; @@ -150,19 +151,19 @@ public class AsyncPregenMethod implements PregeneratorMethod { public static void increaseWorkerThreads() { THREAD_COUNT.updateAndGet(i -> { if (i > 0) return 1; + var adjusted = IrisSettings.get().getConcurrency().getWorldGenThreads(); try { var field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL"); var pool = field.get(null); var threads = ((Thread[]) pool.getClass().getDeclaredMethod("getCoreThreads").invoke(pool)).length; - var adjusted = IrisSettings.get().getConcurrency().getWorldGenThreads(); if (threads >= adjusted) return 0; pool.getClass().getDeclaredMethod("adjustThreadCount", int.class).invoke(pool, adjusted); return threads; - } catch (ClassNotFoundException ignored) { } catch (Throwable e) { - Iris.error("Failed to increase worker threads"); - e.printStackTrace(); + Iris.warn("Failed to increase worker threads, please increase it manually to " + adjusted); + Iris.warn("For more information see https://docs.papermc.io/paper/reference/global-configuration#chunk_system_worker_threads"); + if (e instanceof InvocationTargetException) e.printStackTrace(); } return 0; }); @@ -177,7 +178,6 @@ public class AsyncPregenMethod implements PregeneratorMethod { var method = pool.getClass().getDeclaredMethod("adjustThreadCount", int.class); method.invoke(pool, i); return 0; - } catch (ClassNotFoundException ignored) { } catch (Throwable e) { Iris.error("Failed to reset worker threads"); e.printStackTrace(); From 71078a20a9917f080bba15cc1a3648e42246095b Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 8 Apr 2025 21:15:19 +0200 Subject: [PATCH 004/138] Don't inject world load context for main worlds to prevent incompatibilities --- .../com/volmit/iris/core/nms/INMSBinding.java | 4 +- .../iris/core/nms/v1X/NMSBinding1X.java | 4 +- .../engine/object/IrisContextInjector.java | 23 ++---- .../volmit/iris/util/collection/KList.java | 6 ++ .../iris/core/nms/v1_20_R1/NMSBinding.java | 70 +++++++------------ .../iris/core/nms/v1_20_R2/NMSBinding.java | 70 +++++++------------ .../iris/core/nms/v1_20_R3/NMSBinding.java | 70 +++++++------------ .../iris/core/nms/v1_20_R4/NMSBinding.java | 70 +++++++------------ .../iris/core/nms/v1_21_R1/NMSBinding.java | 70 +++++++------------ .../iris/core/nms/v1_21_R2/NMSBinding.java | 70 +++++++------------ .../iris/core/nms/v1_21_R3/NMSBinding.java | 70 +++++++------------ 11 files changed, 192 insertions(+), 335 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java index d7a28f822..ecf15ace6 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java @@ -132,7 +132,9 @@ public interface INMSBinding { AutoClosing injectLevelStems(); - Pair injectUncached(boolean overworld, boolean nether, boolean end); + default AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { + return null; + } boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end); diff --git a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java index ec0626403..a5ace2486 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java +++ b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java @@ -126,8 +126,8 @@ public class NMSBinding1X implements INMSBinding { } @Override - public Pair injectUncached(boolean overworld, boolean nether, boolean end) { - return new Pair<>(0, new AutoClosing(() -> {})); + public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { + return injectLevelStems(); } @Override diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisContextInjector.java b/core/src/main/java/com/volmit/iris/engine/object/IrisContextInjector.java index 3ecafab8a..415dd7ec8 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisContextInjector.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisContextInjector.java @@ -2,12 +2,15 @@ package com.volmit.iris.engine.object; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.container.AutoClosing; +import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.misc.ServerProperties; import lombok.Getter; import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.world.WorldInitEvent; @@ -19,14 +22,9 @@ public class IrisContextInjector implements Listener { @Getter private static boolean missingDimensionTypes = false; private AutoClosing autoClosing = null; - private final int totalWorlds; - private int worldCounter = 0; public IrisContextInjector() { - if (!Bukkit.getWorlds().isEmpty()) { - totalWorlds = 0; - return; - } + if (!Bukkit.getWorlds().isEmpty()) return; String levelName = ServerProperties.LEVEL_NAME; List irisWorlds = irisWorlds(); @@ -34,29 +32,20 @@ public class IrisContextInjector implements Listener { boolean nether = irisWorlds.contains(levelName + "_nether"); boolean end = irisWorlds.contains(levelName + "_end"); - int i = 1; - if (Bukkit.getAllowNether()) i++; - if (Bukkit.getAllowEnd()) i++; - if (INMS.get().missingDimensionTypes(overworld, nether, end)) { missingDimensionTypes = true; - totalWorlds = 0; return; } if (overworld || nether || end) { - var pair = INMS.get().injectUncached(overworld, nether, end); - i += pair.getA() - 3; - autoClosing = pair.getB(); + autoClosing = INMS.get().injectUncached(overworld, nether, end); } - totalWorlds = i; instance.registerListener(this); } - @EventHandler + @EventHandler(priority = EventPriority.LOWEST) public void on(WorldInitEvent event) { - if (++worldCounter < totalWorlds) return; if (autoClosing != null) { autoClosing.close(); autoClosing = null; diff --git a/core/src/main/java/com/volmit/iris/util/collection/KList.java b/core/src/main/java/com/volmit/iris/util/collection/KList.java index 9e00ea20d..bf1cf916d 100644 --- a/core/src/main/java/com/volmit/iris/util/collection/KList.java +++ b/core/src/main/java/com/volmit/iris/util/collection/KList.java @@ -26,6 +26,8 @@ import com.volmit.iris.util.math.RNG; import java.util.*; import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.Collector; +import java.util.stream.Collectors; @SuppressWarnings("ALL") public class KList extends ArrayList implements List { @@ -65,6 +67,10 @@ public class KList extends ArrayList implements List { return s; } + public static Collector> collector() { + return Collectors.toCollection(KList::new); + } + public static KList asStringList(List oo) { KList s = new KList(); diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java index 2e79fabf0..8f974008b 100644 --- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java @@ -645,37 +645,44 @@ public class NMSBinding implements INMSBinding { } @Override + @SneakyThrows public AutoClosing injectLevelStems() { - return inject(this::supplier); + if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + + var server = ((CraftServer) Bukkit.getServer()); + var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); + var nmsServer = server.getServer(); + var old = nmsServer.worldLoader; + + field.setAccessible(true); + field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( + old.resources(), + old.dataConfiguration(), + old.datapackWorldgen(), + createRegistryAccess(old.datapackDimensions(), false, true, true, true) + ))); + + return new AutoClosing(() -> { + field.set(nmsServer, old); + dataContextLock.unlock(); + }); } @Override @SneakyThrows - public com.volmit.iris.core.nms.container.Pair injectUncached(boolean overworld, boolean nether, boolean end) { + public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { var reg = registry(); var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); field.setAccessible(true); - AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end) - ) - ); - - var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM); + var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); + var injected = access.registryOrThrow(Registries.LEVEL_STEM); var old = (Map>, Registry>) field.get(reg); var fake = new HashMap<>(old); fake.put(Registries.LEVEL_STEM, injected); field.set(reg, fake); - return new com.volmit.iris.core.nms.container.Pair<>( - injected.size(), - new AutoClosing(() -> { - closing.close(); - field.set(reg, old); - })); + return new AutoClosing(() -> field.set(reg, old)); } @Override @@ -692,33 +699,6 @@ public class NMSBinding implements INMSBinding { ((CraftWorld) world).getHandle().K.customDimensions = null; } - private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { - return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - )); - } - - @SneakyThrows - private AutoClosing inject(Function transformer) { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); - - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, transformer.apply(old)); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { var access = registry(); var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE); @@ -740,7 +720,7 @@ public class NMSBinding implements INMSBinding { if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM)); - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze(); + return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); } private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java index b587280d4..c68a8a19e 100644 --- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java @@ -646,37 +646,44 @@ public class NMSBinding implements INMSBinding { } @Override + @SneakyThrows public AutoClosing injectLevelStems() { - return inject(this::supplier); + if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + + var server = ((CraftServer) Bukkit.getServer()); + var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); + var nmsServer = server.getServer(); + var old = nmsServer.worldLoader; + + field.setAccessible(true); + field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( + old.resources(), + old.dataConfiguration(), + old.datapackWorldgen(), + createRegistryAccess(old.datapackDimensions(), false, true, true, true) + ))); + + return new AutoClosing(() -> { + field.set(nmsServer, old); + dataContextLock.unlock(); + }); } @Override @SneakyThrows - public com.volmit.iris.core.nms.container.Pair injectUncached(boolean overworld, boolean nether, boolean end) { + public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { var reg = registry(); var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); field.setAccessible(true); - AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end) - ) - ); - - var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM); + var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); + var injected = access.registryOrThrow(Registries.LEVEL_STEM); var old = (Map>, Registry>) field.get(reg); var fake = new HashMap<>(old); fake.put(Registries.LEVEL_STEM, injected); field.set(reg, fake); - return new com.volmit.iris.core.nms.container.Pair<>( - injected.size(), - new AutoClosing(() -> { - closing.close(); - field.set(reg, old); - })); + return new AutoClosing(() -> field.set(reg, old)); } @Override @@ -693,33 +700,6 @@ public class NMSBinding implements INMSBinding { ((CraftWorld) world).getHandle().K.customDimensions = null; } - private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { - return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - )); - } - - @SneakyThrows - private AutoClosing inject(Function transformer) { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); - - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, transformer.apply(old)); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { var access = registry(); var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE); @@ -741,7 +721,7 @@ public class NMSBinding implements INMSBinding { if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM)); - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze(); + return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); } private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java index 63efb9984..2d0e08931 100644 --- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java @@ -647,37 +647,44 @@ public class NMSBinding implements INMSBinding { } @Override + @SneakyThrows public AutoClosing injectLevelStems() { - return inject(this::supplier); + if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + + var server = ((CraftServer) Bukkit.getServer()); + var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); + var nmsServer = server.getServer(); + var old = nmsServer.worldLoader; + + field.setAccessible(true); + field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( + old.resources(), + old.dataConfiguration(), + old.datapackWorldgen(), + createRegistryAccess(old.datapackDimensions(), false, true, true, true) + ))); + + return new AutoClosing(() -> { + field.set(nmsServer, old); + dataContextLock.unlock(); + }); } @Override @SneakyThrows - public com.volmit.iris.core.nms.container.Pair injectUncached(boolean overworld, boolean nether, boolean end) { + public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { var reg = registry(); var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); field.setAccessible(true); - AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end) - ) - ); - - var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM); + var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); + var injected = access.registryOrThrow(Registries.LEVEL_STEM); var old = (Map>, Registry>) field.get(reg); var fake = new HashMap<>(old); fake.put(Registries.LEVEL_STEM, injected); field.set(reg, fake); - return new com.volmit.iris.core.nms.container.Pair<>( - injected.size(), - new AutoClosing(() -> { - closing.close(); - field.set(reg, old); - })); + return new AutoClosing(() -> field.set(reg, old)); } @Override @@ -694,33 +701,6 @@ public class NMSBinding implements INMSBinding { ((CraftWorld) world).getHandle().K.customDimensions = null; } - private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { - return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - )); - } - - @SneakyThrows - private AutoClosing inject(Function transformer) { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); - - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, transformer.apply(old)); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { var access = registry(); var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE); @@ -742,7 +722,7 @@ public class NMSBinding implements INMSBinding { if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM)); - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze(); + return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); } private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java index db25b3bc7..ec076e7df 100644 --- a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java @@ -672,37 +672,44 @@ public class NMSBinding implements INMSBinding { } @Override + @SneakyThrows public AutoClosing injectLevelStems() { - return inject(this::supplier); + if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + + var server = ((CraftServer) Bukkit.getServer()); + var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); + var nmsServer = server.getServer(); + var old = nmsServer.worldLoader; + + field.setAccessible(true); + field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( + old.resources(), + old.dataConfiguration(), + old.datapackWorldgen(), + createRegistryAccess(old.datapackDimensions(), false, true, true, true) + ))); + + return new AutoClosing(() -> { + field.set(nmsServer, old); + dataContextLock.unlock(); + }); } @Override @SneakyThrows - public com.volmit.iris.core.nms.container.Pair injectUncached(boolean overworld, boolean nether, boolean end) { + public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { var reg = registry(); var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); field.setAccessible(true); - AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end) - ) - ); - - var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM); + var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); + var injected = access.registryOrThrow(Registries.LEVEL_STEM); var old = (Map>, Registry>) field.get(reg); var fake = new HashMap<>(old); fake.put(Registries.LEVEL_STEM, injected); field.set(reg, fake); - return new com.volmit.iris.core.nms.container.Pair<>( - injected.size(), - new AutoClosing(() -> { - closing.close(); - field.set(reg, old); - })); + return new AutoClosing(() -> field.set(reg, old)); } @Override @@ -719,33 +726,6 @@ public class NMSBinding implements INMSBinding { ((CraftWorld) world).getHandle().K.customDimensions = null; } - private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { - return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - )); - } - - @SneakyThrows - private AutoClosing inject(Function transformer) { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); - - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, transformer.apply(old)); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { var access = registry(); var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE); @@ -767,7 +747,7 @@ public class NMSBinding implements INMSBinding { if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM)); - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze(); + return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); } private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java index 09380f1d4..7613bf71f 100644 --- a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java +++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java @@ -676,37 +676,44 @@ public class NMSBinding implements INMSBinding { } @Override + @SneakyThrows public AutoClosing injectLevelStems() { - return inject(this::supplier); + if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + + var server = ((CraftServer) Bukkit.getServer()); + var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); + var nmsServer = server.getServer(); + var old = nmsServer.worldLoader; + + field.setAccessible(true); + field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( + old.resources(), + old.dataConfiguration(), + old.datapackWorldgen(), + createRegistryAccess(old.datapackDimensions(), false, true, true, true) + ))); + + return new AutoClosing(() -> { + field.set(nmsServer, old); + dataContextLock.unlock(); + }); } @Override @SneakyThrows - public com.volmit.iris.core.nms.container.Pair injectUncached(boolean overworld, boolean nether, boolean end) { + public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { var reg = registry(); var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); field.setAccessible(true); - AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end) - ) - ); - - var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM); + var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); + var injected = access.registryOrThrow(Registries.LEVEL_STEM); var old = (Map>, Registry>) field.get(reg); var fake = new HashMap<>(old); fake.put(Registries.LEVEL_STEM, injected); field.set(reg, fake); - return new com.volmit.iris.core.nms.container.Pair<>( - injected.size(), - new AutoClosing(() -> { - closing.close(); - field.set(reg, old); - })); + return new AutoClosing(() -> field.set(reg, old)); } @Override @@ -723,33 +730,6 @@ public class NMSBinding implements INMSBinding { ((CraftWorld) world).getHandle().K.customDimensions = null; } - private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { - return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - )); - } - - @SneakyThrows - private AutoClosing inject(Function transformer) { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); - - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, transformer.apply(old)); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { var access = registry(); var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE); @@ -771,7 +751,7 @@ public class NMSBinding implements INMSBinding { if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM)); - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze(); + return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); } private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { diff --git a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java index 0447a4b11..41b75953b 100644 --- a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java +++ b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java @@ -666,37 +666,44 @@ public class NMSBinding implements INMSBinding { } @Override + @SneakyThrows public AutoClosing injectLevelStems() { - return inject(this::supplier); + if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + + var server = ((CraftServer) Bukkit.getServer()); + var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); + var nmsServer = server.getServer(); + var old = nmsServer.worldLoader; + + field.setAccessible(true); + field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( + old.resources(), + old.dataConfiguration(), + old.datapackWorldgen(), + createRegistryAccess(old.datapackDimensions(), false, true, true, true) + ))); + + return new AutoClosing(() -> { + field.set(nmsServer, old); + dataContextLock.unlock(); + }); } @Override @SneakyThrows - public Pair injectUncached(boolean overworld, boolean nether, boolean end) { + public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { var reg = registry(); var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); field.setAccessible(true); - AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end) - ) - ); - - var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().lookupOrThrow(Registries.LEVEL_STEM); + var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); + var injected = access.lookupOrThrow(Registries.LEVEL_STEM); var old = (Map>, Registry>) field.get(reg); var fake = new HashMap<>(old); fake.put(Registries.LEVEL_STEM, injected); field.set(reg, fake); - return new Pair<>( - injected.size(), - new AutoClosing(() -> { - closing.close(); - field.set(reg, old); - })); + return new AutoClosing(() -> field.set(reg, old)); } @Override @@ -713,33 +720,6 @@ public class NMSBinding implements INMSBinding { ((CraftWorld) world).getHandle().L.customDimensions = null; } - private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { - return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - )); - } - - @SneakyThrows - private AutoClosing inject(Function transformer) { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); - - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, transformer.apply(old)); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { var access = registry(); var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE); @@ -761,7 +741,7 @@ public class NMSBinding implements INMSBinding { if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM)); - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze(); + return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); } private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { diff --git a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java index 80a3fdc69..c6ba0e6dd 100644 --- a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java +++ b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java @@ -665,37 +665,44 @@ public class NMSBinding implements INMSBinding { } @Override + @SneakyThrows public AutoClosing injectLevelStems() { - return inject(this::supplier); + if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + + var server = ((CraftServer) Bukkit.getServer()); + var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); + var nmsServer = server.getServer(); + var old = nmsServer.worldLoader; + + field.setAccessible(true); + field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( + old.resources(), + old.dataConfiguration(), + old.datapackWorldgen(), + createRegistryAccess(old.datapackDimensions(), false, true, true, true) + ))); + + return new AutoClosing(() -> { + field.set(nmsServer, old); + dataContextLock.unlock(); + }); } @Override @SneakyThrows - public Pair injectUncached(boolean overworld, boolean nether, boolean end) { + public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { var reg = registry(); var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); field.setAccessible(true); - AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end) - ) - ); - - var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().lookupOrThrow(Registries.LEVEL_STEM); + var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); + var injected = access.lookupOrThrow(Registries.LEVEL_STEM); var old = (Map>, Registry>) field.get(reg); var fake = new HashMap<>(old); fake.put(Registries.LEVEL_STEM, injected); field.set(reg, fake); - return new Pair<>( - injected.size(), - new AutoClosing(() -> { - closing.close(); - field.set(reg, old); - })); + return new AutoClosing(() -> field.set(reg, old)); } @Override @@ -712,33 +719,6 @@ public class NMSBinding implements INMSBinding { ((CraftWorld) world).getHandle().L.customDimensions = null; } - private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { - return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - )); - } - - @SneakyThrows - private AutoClosing inject(Function transformer) { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); - - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, transformer.apply(old)); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { var access = registry(); var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE); @@ -760,7 +740,7 @@ public class NMSBinding implements INMSBinding { if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM)); - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze(); + return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); } private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { From 9a691ac5b45ca6b5b7d05e3d5796036323063946 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 9 Apr 2025 10:57:35 +0200 Subject: [PATCH 005/138] setup runServer for all supported versions --- build.gradle | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index da1f2c218..094bbb56b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,5 @@ +import xyz.jpenilla.runpaper.task.RunServer + /* * Iris is a World Generator for Minecraft Bukkit Servers * Copyright (c) 2021 Arcane Arts (Volmit Software) @@ -30,6 +32,7 @@ plugins { id 'java-library' id "io.github.goooler.shadow" version "8.1.7" id "de.undercouch.download" version "5.0.1" + id "xyz.jpenilla.run-paper" version "2.3.1" } @@ -53,6 +56,11 @@ registerCustomOutputTaskUnix('PixelMac', '/Users/test/Desktop/mcserver/plugins') registerCustomOutputTaskUnix('CrazyDev22LT', '/home/julian/Desktop/server/plugins') // ============================================================== +def MIN_HEAP_SIZE = "2G" +def MAX_HEAP_SIZE = "8G" +//Valid values are: none, truecolor, indexed256, indexed16, indexed8 +def COLOR = "truecolor" + def NMS_BINDINGS = Map.of( "v1_21_R3", "1.21.4-R0.1-SNAPSHOT", "v1_21_R2", "1.21.3-R0.1-SNAPSHOT", @@ -62,21 +70,34 @@ def NMS_BINDINGS = Map.of( "v1_20_R2", "1.20.2-R0.1-SNAPSHOT", "v1_20_R1", "1.20.1-R0.1-SNAPSHOT", ) -def JVM_VERSION = Map.of() -NMS_BINDINGS.each { nms -> - project(":nms:${nms.key}") { +def JVM_VERSION = Map.of() +NMS_BINDINGS.forEach { key, value -> + project(":nms:$key") { apply plugin: 'java' apply plugin: 'com.volmit.nmstools' nmsTools { - it.jvm = JVM_VERSION.getOrDefault(nms.key, 21) - it.version = nms.value + it.jvm = JVM_VERSION.getOrDefault(key, 21) + it.version = value } dependencies { implementation project(":core") } } + + tasks.register("runServer-$key", RunServer) { + group("servers") + minecraftVersion(value.split("-")[0]) + minHeapSize(MIN_HEAP_SIZE) + maxHeapSize(MAX_HEAP_SIZE) + pluginJars(tasks.shadowJar.archiveFile) + javaLauncher = javaToolchains.launcherFor { it.languageVersion = JavaLanguageVersion.of(JVM_VERSION.getOrDefault(key, 21))} + runDirectory.convention(layout.buildDirectory.dir("run/$key")) + systemProperty("disable.watchdog", "") + systemProperty("net.kyori.ansi.colorLevel", COLOR) + systemProperty("com.mojang.eula.agree", true) + } } shadowJar { From 536d20bca7c344251ad9f16354bf2ec040e0b8cf Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 9 Apr 2025 12:16:03 +0200 Subject: [PATCH 006/138] remove context injection on world init --- .../com/volmit/iris/core/nms/INMSBinding.java | 2 +- .../iris/core/nms/container/AutoClosing.java | 17 +++++++++++++++++ .../engine/platform/BukkitChunkGenerator.java | 5 ++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java index ecf15ace6..1b881e6fe 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java @@ -20,7 +20,6 @@ package com.volmit.iris.core.nms; import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; -import com.volmit.iris.core.nms.container.Pair; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KList; @@ -94,6 +93,7 @@ public interface INMSBinding { throw new IllegalStateException("Missing dimenstion types to create world"); try (var ignored = injectLevelStems()) { + ignored.storeContext(); return c.createWorld(); } } diff --git a/core/src/main/java/com/volmit/iris/core/nms/container/AutoClosing.java b/core/src/main/java/com/volmit/iris/core/nms/container/AutoClosing.java index afa2ba9dc..a4771ca84 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/container/AutoClosing.java +++ b/core/src/main/java/com/volmit/iris/core/nms/container/AutoClosing.java @@ -1,5 +1,6 @@ package com.volmit.iris.core.nms.container; +import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.function.NastyRunnable; import lombok.AllArgsConstructor; @@ -7,6 +8,7 @@ import java.util.concurrent.atomic.AtomicBoolean; @AllArgsConstructor public class AutoClosing implements AutoCloseable { + private static final KMap CONTEXTS = new KMap<>(); private final AtomicBoolean closed = new AtomicBoolean(); private final NastyRunnable action; @@ -14,9 +16,24 @@ public class AutoClosing implements AutoCloseable { public void close() { if (closed.getAndSet(true)) return; try { + removeContext(); action.run(); } catch (Throwable e) { throw new RuntimeException(e); } } + + public void storeContext() { + CONTEXTS.put(Thread.currentThread(), this); + } + + public void removeContext() { + CONTEXTS.values().removeIf(c -> c == this); + } + + public static void closeContext() { + AutoClosing closing = CONTEXTS.remove(Thread.currentThread()); + if (closing == null) return; + closing.close(); + } } diff --git a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index 9b57072c2..90117a5a4 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -21,6 +21,7 @@ package com.volmit.iris.engine.platform; import com.volmit.iris.Iris; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.engine.IrisEngine; import com.volmit.iris.engine.data.chunk.TerrainChunk; @@ -48,6 +49,7 @@ import org.bukkit.block.data.BlockData; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.world.WorldInitEvent; import org.bukkit.generator.BiomeProvider; @@ -122,11 +124,12 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun } } - @EventHandler + @EventHandler(priority = EventPriority.LOWEST) public void onWorldInit(WorldInitEvent event) { try { if (initialized || !world.name().equals(event.getWorld().getName())) return; + AutoClosing.closeContext(); INMS.get().removeCustomDimensions(event.getWorld()); world.setRawWorldSeed(event.getWorld().getSeed()); Engine engine = getEngine(event.getWorld()); From ab4770400eb92d10517e949d68927ef8f8e7f5b4 Mon Sep 17 00:00:00 2001 From: Aidan Aeternum Date: Thu, 10 Apr 2025 04:49:32 -0400 Subject: [PATCH 007/138] v+ --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 094bbb56b..ebe09d7d8 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ plugins { } -version '3.6.5-1.20.1-1.21.4' +version '3.6.6-1.20.1-1.21.4' // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED // ======================== WINDOWS ============================= From 9c151abac781b046e741c1cb62f31388a3662493 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Thu, 10 Apr 2025 16:26:58 +0200 Subject: [PATCH 008/138] replace world load context injection with bytecode injections --- build.gradle | 10 + core/src/main/java/com/volmit/iris/Iris.java | 4 - .../com/volmit/iris/core/nms/INMSBinding.java | 27 +-- .../iris/core/nms/v1X/NMSBinding1X.java | 14 +- .../iris/core/safeguard/ServerBootSFG.java | 58 ++++- .../volmit/iris/core/safeguard/UtilsSFG.java | 5 + .../engine/object/IrisContextInjector.java | 66 ------ .../engine/platform/BukkitChunkGenerator.java | 1 - .../com/volmit/iris/util/agent/Agent.java | 41 ++++ .../com/volmit/iris/util/agent/Installer.java | 29 +++ core/src/main/resources/plugin.yml | 2 + .../iris/core/nms/v1_20_R1/NMSBinding.java | 201 +++++++++--------- .../iris/core/nms/v1_20_R2/NMSBinding.java | 198 ++++++++--------- .../iris/core/nms/v1_20_R3/NMSBinding.java | 198 ++++++++--------- .../iris/core/nms/v1_20_R4/NMSBinding.java | 195 +++++++++-------- .../iris/core/nms/v1_21_R1/NMSBinding.java | 199 ++++++++--------- .../iris/core/nms/v1_21_R2/NMSBinding.java | 190 +++++++++-------- .../iris/core/nms/v1_21_R3/NMSBinding.java | 190 +++++++++-------- 18 files changed, 861 insertions(+), 767 deletions(-) delete mode 100644 core/src/main/java/com/volmit/iris/engine/object/IrisContextInjector.java create mode 100644 core/src/main/java/com/volmit/iris/util/agent/Agent.java create mode 100644 core/src/main/java/com/volmit/iris/util/agent/Installer.java diff --git a/build.gradle b/build.gradle index ebe09d7d8..c7370bc97 100644 --- a/build.gradle +++ b/build.gradle @@ -97,6 +97,7 @@ NMS_BINDINGS.forEach { key, value -> systemProperty("disable.watchdog", "") systemProperty("net.kyori.ansi.colorLevel", COLOR) systemProperty("com.mojang.eula.agree", true) + jvmArgs("-javaagent:${tasks.shadowJar.archiveFile.get().asFile.absolutePath}") } } @@ -113,6 +114,12 @@ shadowJar { relocate 'net.kyori', 'com.volmit.iris.util.kyori' relocate 'org.bstats', 'com.volmit.util.metrics' archiveFileName.set("Iris-${project.version}.jar") + manifest.attributes( + 'Agent-Class': 'com.volmit.iris.util.agent.Installer', + 'Premain-Class': 'com.volmit.iris.util.agent.Installer', + 'Can-Redefine-Classes': true, + 'Can-Retransform-Classes': true + ) } dependencies { @@ -173,6 +180,9 @@ allprojects { compileOnly 'com.github.ben-manes.caffeine:caffeine:3.0.6' compileOnly 'org.apache.commons:commons-lang3:3.12.0' compileOnly 'com.github.oshi:oshi-core:6.6.5' + + compileOnly("net.bytebuddy:byte-buddy:1.17.5") + compileOnly("net.bytebuddy:byte-buddy-agent:1.17.5") } /** diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 07e01c6e4..e059b30e1 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -32,10 +32,8 @@ import com.volmit.iris.core.nms.v1X.NMSBinding1X; import com.volmit.iris.core.pregenerator.LazyPregenerator; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.tools.IrisToolbelt; -import com.volmit.iris.core.tools.IrisWorldCreator; import com.volmit.iris.engine.EnginePanic; import com.volmit.iris.engine.object.IrisCompat; -import com.volmit.iris.engine.object.IrisContextInjector; import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisWorld; import com.volmit.iris.engine.platform.BukkitChunkGenerator; @@ -457,11 +455,9 @@ public class Iris extends VolmitPlugin implements Listener { services = new KMap<>(); setupAudience(); initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class) i.getClass(), (IrisService) i)); - INMS.get(); IO.delete(new File("iris")); compat = IrisCompat.configured(getDataFile("compat.json")); ServerConfigurator.configure(); - new IrisContextInjector(); IrisSafeguard.IrisSafeguardSystem(); getSender().setTag(getTag()); IrisSafeguard.earlySplash(); diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java index 1b881e6fe..8c57cb625 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java @@ -18,7 +18,6 @@ package com.volmit.iris.core.nms; -import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.framework.Engine; @@ -89,13 +88,10 @@ public interface INMSBinding { MCABiomeContainer newBiomeContainer(int min, int max); default World createWorld(WorldCreator c) { - if (missingDimensionTypes(true, true, true)) + if (missingDimensionTypes(c.environment())) throw new IllegalStateException("Missing dimenstion types to create world"); - try (var ignored = injectLevelStems()) { - ignored.storeContext(); - return c.createWorld(); - } + return c.createWorld(); } int countCustomBiomes(); @@ -130,13 +126,20 @@ public interface INMSBinding { KList getStructureKeys(); - AutoClosing injectLevelStems(); - - default AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { - return null; - } + KMap getMainWorlds(); boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end); - void removeCustomDimensions(World world); + default boolean missingDimensionTypes(World.Environment env) { + return switch (env) { + case NORMAL -> missingDimensionTypes(true, false, false); + case NETHER -> missingDimensionTypes(false, true, false); + case THE_END -> missingDimensionTypes(false, false, true); + default -> true; + }; + } + + default boolean injectBukkit() { + return true; + } } diff --git a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java index a5ace2486..90e68c549 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java +++ b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java @@ -121,13 +121,8 @@ public class NMSBinding1X implements INMSBinding { } @Override - public AutoClosing injectLevelStems() { - return new AutoClosing(() -> {}); - } - - @Override - public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { - return injectLevelStems(); + public KMap getMainWorlds() { + return new KMap<>(); } @Override @@ -135,11 +130,6 @@ public class NMSBinding1X implements INMSBinding { return false; } - @Override - public void removeCustomDimensions(World world) { - - } - @Override public CompoundTag serializeEntity(Entity location) { return null; diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java index 2c59c2ae9..9ee9a4d2e 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java @@ -3,8 +3,12 @@ package com.volmit.iris.core.safeguard; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.v1X.NMSBinding1X; -import com.volmit.iris.engine.object.IrisContextInjector; +import com.volmit.iris.util.agent.Agent; +import com.volmit.iris.util.misc.ServerProperties; import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import javax.tools.JavaCompiler; @@ -15,10 +19,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.StringJoiner; +import java.util.*; +import java.util.function.Predicate; import static com.volmit.iris.Iris.getJavaVersion; import static com.volmit.iris.core.safeguard.IrisSafeguard.*; @@ -31,6 +33,8 @@ public class ServerBootSFG { public static boolean hasPrivileges = true; public static boolean unsuportedversion = false; public static boolean missingDimensionTypes = false; + public static boolean missingAgent = false; + public static boolean failedInjection = false; protected static boolean safeguardPassed; public static boolean passedserversoftware = true; protected static int count; @@ -112,10 +116,21 @@ public class ServerBootSFG { severityMedium++; } - if (IrisContextInjector.isMissingDimensionTypes()) { - missingDimensionTypes = true; - joiner.add("Missing Dimension Types"); + if (!Agent.install()) { + missingAgent = true; + joiner.add("Missing Java Agent"); severityHigh++; + } else { + if (missingDimensionTypes()) { + missingDimensionTypes = true; + joiner.add("Missing Dimension Types"); + severityHigh++; + } + if (!INMS.get().injectBukkit()) { + failedInjection = true; + joiner.add("Failed Bukkit Injection"); + severityHigh++; + } } allIncompatibilities = joiner.toString(); @@ -173,4 +188,31 @@ public class ServerBootSFG { return !path.isEmpty() && (new File(path, "javac").exists() || new File(path, "javac.exe").exists()); } + private static boolean missingDimensionTypes() { + var irisWorlds = irisWorlds(); + if (irisWorlds.isEmpty()) return false; + + var worlds = INMS.get().getMainWorlds(); + worlds.keySet().removeIf(Predicate.not(irisWorlds::contains)); + + boolean overworld = worlds.containsValue(World.Environment.NORMAL) || worlds.containsValue(World.Environment.CUSTOM); + boolean nether = worlds.containsValue(World.Environment.NETHER); + boolean end = worlds.containsValue(World.Environment.THE_END); + + if (overworld || nether || end) + return INMS.get().missingDimensionTypes(overworld, nether, end); + return false; + } + + private static List irisWorlds() { + var config = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML); + ConfigurationSection section = config.getConfigurationSection("worlds"); + if (section == null) return List.of(); + + return section.getKeys(false) + .stream() + .filter(k -> section.getString(k + ".generator", "").startsWith("Iris")) + .toList(); + } + } diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java index c45cfc7bb..e3deb6f2b 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java @@ -44,6 +44,11 @@ public class UtilsSFG { Iris.safeguard(C.RED + "- Required Iris dimension types were not loaded."); Iris.safeguard(C.RED + "- If this still happens after a restart please contact support."); } + if (ServerBootSFG.missingAgent) { + Iris.safeguard(C.RED + "Java Agent"); + Iris.safeguard(C.RED + "- Please enable dynamic agent loading by adding -XX:+EnableDynamicAgentLoading to your jvm arguments."); + Iris.safeguard(C.RED + "- or add the jvm argument -javaagent:plugins/" + Iris.instance.getJarFile().getName()); + } if (!ServerBootSFG.passedserversoftware) { Iris.safeguard(C.YELLOW + "Unsupported Server Software"); Iris.safeguard(C.YELLOW + "- Please consider using Paper or Purpur instead."); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisContextInjector.java b/core/src/main/java/com/volmit/iris/engine/object/IrisContextInjector.java deleted file mode 100644 index 415dd7ec8..000000000 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisContextInjector.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.volmit.iris.engine.object; - -import com.volmit.iris.core.nms.INMS; -import com.volmit.iris.core.nms.container.AutoClosing; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.misc.ServerProperties; -import lombok.Getter; -import org.bukkit.Bukkit; -import org.bukkit.NamespacedKey; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.world.WorldInitEvent; - -import java.util.List; - -import static com.volmit.iris.Iris.instance; - -public class IrisContextInjector implements Listener { - @Getter - private static boolean missingDimensionTypes = false; - private AutoClosing autoClosing = null; - - public IrisContextInjector() { - if (!Bukkit.getWorlds().isEmpty()) return; - - String levelName = ServerProperties.LEVEL_NAME; - List irisWorlds = irisWorlds(); - boolean overworld = irisWorlds.contains(levelName); - boolean nether = irisWorlds.contains(levelName + "_nether"); - boolean end = irisWorlds.contains(levelName + "_end"); - - if (INMS.get().missingDimensionTypes(overworld, nether, end)) { - missingDimensionTypes = true; - return; - } - - if (overworld || nether || end) { - autoClosing = INMS.get().injectUncached(overworld, nether, end); - } - - instance.registerListener(this); - } - - @EventHandler(priority = EventPriority.LOWEST) - public void on(WorldInitEvent event) { - if (autoClosing != null) { - autoClosing.close(); - autoClosing = null; - } - instance.unregisterListener(this); - } - - private List irisWorlds() { - var config = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML); - ConfigurationSection section = config.getConfigurationSection("worlds"); - if (section == null) return List.of(); - - return section.getKeys(false) - .stream() - .filter(k -> section.getString(k + ".generator", "").startsWith("Iris")) - .toList(); - } -} diff --git a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index 90117a5a4..4285dd799 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -130,7 +130,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun if (initialized || !world.name().equals(event.getWorld().getName())) return; AutoClosing.closeContext(); - INMS.get().removeCustomDimensions(event.getWorld()); world.setRawWorldSeed(event.getWorld().getSeed()); Engine engine = getEngine(event.getWorld()); if (engine == null) { diff --git a/core/src/main/java/com/volmit/iris/util/agent/Agent.java b/core/src/main/java/com/volmit/iris/util/agent/Agent.java new file mode 100644 index 000000000..009923b30 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/agent/Agent.java @@ -0,0 +1,41 @@ +package com.volmit.iris.util.agent; + +import com.volmit.iris.Iris; +import net.bytebuddy.agent.ByteBuddyAgent; +import net.bytebuddy.dynamic.loading.ClassReloadingStrategy; + +import java.lang.instrument.Instrumentation; + +public class Agent { + private static final String NAME = "com.volmit.iris.util.agent.Installer"; + + public static ClassReloadingStrategy installed() { + return ClassReloadingStrategy.of(getInstrumentation()); + } + + public static Instrumentation getInstrumentation() { + Instrumentation instrumentation = doGetInstrumentation(); + if (instrumentation == null) throw new IllegalStateException("The agent is not initialized or unavailable"); + return instrumentation; + } + + public static boolean install() { + if (doGetInstrumentation() != null) + return true; + try { + Iris.info("Installing Java Agent..."); + ByteBuddyAgent.attach(Iris.instance.getJarFile(), ByteBuddyAgent.ProcessProvider.ForCurrentVm.INSTANCE); + } catch (Throwable e) { + e.printStackTrace(); + } + return doGetInstrumentation() != null; + } + + private static Instrumentation doGetInstrumentation() { + try { + return (Instrumentation) Class.forName(NAME, true, ClassLoader.getSystemClassLoader()).getMethod("getInstrumentation").invoke(null); + } catch (Exception ex) { + return null; + } + } +} diff --git a/core/src/main/java/com/volmit/iris/util/agent/Installer.java b/core/src/main/java/com/volmit/iris/util/agent/Installer.java new file mode 100644 index 000000000..3c68fd579 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/agent/Installer.java @@ -0,0 +1,29 @@ +package com.volmit.iris.util.agent; + +import java.lang.instrument.Instrumentation; + +public class Installer { + private static volatile Instrumentation instrumentation; + + public static Instrumentation getInstrumentation() { + Instrumentation instrumentation = Installer.instrumentation; + if (instrumentation == null) { + throw new IllegalStateException("The agent is not loaded or this method is not called via the system class loader"); + } + return instrumentation; + } + + public static void premain(String arguments, Instrumentation instrumentation) { + doMain(instrumentation); + } + + public static void agentmain(String arguments, Instrumentation instrumentation) { + doMain(instrumentation); + } + + private static synchronized void doMain(Instrumentation instrumentation) { + if (Installer.instrumentation != null) + return; + Installer.instrumentation = instrumentation; + } +} \ No newline at end of file diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml index 7ff73ff7e..aa75fc983 100644 --- a/core/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -20,6 +20,8 @@ libraries: - bsf:bsf:2.4.0 - org.lz4:lz4-java:1.8.0 - com.github.oshi:oshi-core:6.6.5 + - net.bytebuddy:byte-buddy:1.17.5 + - net.bytebuddy:byte-buddy-agent:1.17.5 commands: iris: aliases: [ ir, irs ] diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java index 8f974008b..f096e5a78 100644 --- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java @@ -2,27 +2,30 @@ package com.volmit.iris.core.nms.v1_20_R1; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.Lifecycle; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; -import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.util.agent.Agent; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.format.C; import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterBiomeInject; -import com.volmit.iris.util.nbt.io.NBTUtil; +import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.nbt.mca.NBTWorld; import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; import it.unimi.dsi.fastutil.objects.Object2IntMap; import lombok.SneakyThrows; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.*; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; @@ -31,13 +34,12 @@ import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.WorldLoader; import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.tags.TagKey; -import net.minecraft.world.entity.EntityDimensions; +import net.minecraft.world.RandomSequences; import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; @@ -52,6 +54,8 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -63,31 +67,23 @@ import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlock; import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockState; import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockStates; import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData; -import org.bukkit.craftbukkit.v1_20_R1.entity.CraftDolphin; import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; -import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; import org.bukkit.event.entity.CreatureSpawnEvent; -import org.bukkit.entity.EntityType; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -import sun.misc.Unsafe; import java.awt.Color; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Function; public class NMSBinding implements INMSBinding { @@ -95,11 +91,11 @@ public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); - private final AtomicCache dataLoadContext = new AtomicCache<>(); + private final AtomicBoolean injected = new AtomicBoolean(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final ReentrantLock dataContextLock = new ReentrantLock(true); + private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -645,44 +641,28 @@ public class NMSBinding implements INMSBinding { } @Override - @SneakyThrows - public AutoClosing injectLevelStems() { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + @SuppressWarnings("all") + public KMap getMainWorlds() { + String levelName = ServerProperties.LEVEL_NAME; + KMap worlds = new KMap<>(); + for (var key : registry().registryOrThrow(Registries.LEVEL_STEM).registryKeySet()) { + World.Environment env = World.Environment.NORMAL; + if (key == LevelStem.NETHER) { + if (!Bukkit.getAllowNether()) + continue; + env = World.Environment.NETHER; + } else if (key == LevelStem.END) { + if (!Bukkit.getAllowEnd()) + continue; + env = World.Environment.THE_END; + } else if (key != LevelStem.OVERWORLD) { + env = World.Environment.CUSTOM; + } - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - ))); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - - @Override - @SneakyThrows - public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { - var reg = registry(); - var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); - field.setAccessible(true); - - var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); - var injected = access.registryOrThrow(Registries.LEVEL_STEM); - var old = (Map>, Registry>) field.get(reg); - var fake = new HashMap<>(old); - fake.put(Registries.LEVEL_STEM, injected); - field.set(reg, fake); - - return new AutoClosing(() -> field.set(reg, old)); + String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); + worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); + } + return worlds; } @Override @@ -695,53 +675,74 @@ public class NMSBinding implements INMSBinding { } @Override - public void removeCustomDimensions(World world) { - ((CraftWorld) world).getHandle().K.customDimensions = null; - } - - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { - var access = registry(); - var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE); - - var settings = new FlatLevelGeneratorSettings( - Optional.empty(), - access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), - List.of() - ); - settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); - settings.updateLayers(); - - var source = new FlatLevelSource(settings); - var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental()); - if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD); - if (nether) register(fake, dimensions, source, LevelStem.NETHER); - if (end) register(fake, dimensions, source, LevelStem.END); - copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null)); - - if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM)); - - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); - } - - private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { - var loc = createIrisKey(key); - target.register(key, new LevelStem( - dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), - source - ), Lifecycle.stable()); - } - - private void copy(MappedRegistry target, Registry source) { - if (source == null) return; - source.registryKeySet().forEach(key -> { - var value = source.get(key); - var info = source.lifecycle(value); - if (value != null && info != null && !target.containsKey(key)) - target.register(key, value, info); - }); + public boolean injectBukkit() { + if (injected.getAndSet(true)) + return true; + try { + Iris.info("Injecting Bukkit"); + new ByteBuddy() + .redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( + MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, + ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, + boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), Agent.installed()); + return true; + } catch (Throwable e) { + Iris.error(C.RED + "Failed to inject Bukkit"); + e.printStackTrace(); + } + return false; } private ResourceLocation createIrisKey(ResourceKey key) { return new ResourceLocation("iris", key.location().getPath()); } + + public LevelStem levelStem(RegistryAccess access, World.Environment env) { + if (env == World.Environment.CUSTOM) + env = World.Environment.NORMAL; + return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); + } + + private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { + return access.registryOrThrow(Registries.DIMENSION_TYPE).getHolderOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", switch (env) { + case NORMAL, CUSTOM -> "overworld"; + case NETHER -> "the_nether"; + case THE_END -> "the_end"; + }))); + } + + private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { + var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of()); + settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); + settings.updateLayers(); + return new FlatLevelSource(settings); + } + + private static class ServerLevelAdvice { + @SneakyThrows + @Advice.OnMethodEnter + static void enter( + @Advice.Argument(0) MinecraftServer server, + @Advice.Argument(3) PrimaryLevelData levelData, + @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem, + @Advice.Argument(12) World.Environment env, + @Advice.Argument(value = 13) ChunkGenerator gen + ) { + if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) + return; + + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) + .invoke(bindings, server.registryAccess(), env); + levelData.customDimensions = null; + } + } } diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java index c68a8a19e..dbba77c83 100644 --- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java @@ -1,32 +1,33 @@ package com.volmit.iris.core.nms.v1_20_R2; import java.awt.Color; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Function; import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.Lifecycle; -import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.util.agent.Agent; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.scheduling.J; import lombok.SneakyThrows; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.*; import net.minecraft.core.Registry; import net.minecraft.nbt.*; import net.minecraft.nbt.Tag; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.WorldLoader; import net.minecraft.server.commands.data.BlockDataAccessor; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.tags.TagKey; +import net.minecraft.world.RandomSequences; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.block.Blocks; @@ -36,6 +37,8 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -45,12 +48,11 @@ import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockState; import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockStates; import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData; -import org.bukkit.craftbukkit.v1_20_R2.entity.CraftDolphin; import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_20_R2.util.CraftNamespacedKey; -import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; @@ -68,7 +70,6 @@ import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterBiomeInject; -import com.volmit.iris.util.nbt.io.NBTUtil; import com.volmit.iris.util.nbt.mca.NBTWorld; import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; @@ -79,24 +80,22 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; -import sun.misc.Unsafe; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); - private final AtomicCache dataLoadContext = new AtomicCache<>(); + private final AtomicBoolean injected = new AtomicBoolean(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final ReentrantLock dataContextLock = new ReentrantLock(true); + private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -646,44 +645,28 @@ public class NMSBinding implements INMSBinding { } @Override - @SneakyThrows - public AutoClosing injectLevelStems() { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + @SuppressWarnings("all") + public KMap getMainWorlds() { + String levelName = ServerProperties.LEVEL_NAME; + KMap worlds = new KMap<>(); + for (var key : registry().registryOrThrow(Registries.LEVEL_STEM).registryKeySet()) { + World.Environment env = World.Environment.NORMAL; + if (key == LevelStem.NETHER) { + if (!Bukkit.getAllowNether()) + continue; + env = World.Environment.NETHER; + } else if (key == LevelStem.END) { + if (!Bukkit.getAllowEnd()) + continue; + env = World.Environment.THE_END; + } else if (key != LevelStem.OVERWORLD) { + env = World.Environment.CUSTOM; + } - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - ))); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - - @Override - @SneakyThrows - public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { - var reg = registry(); - var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); - field.setAccessible(true); - - var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); - var injected = access.registryOrThrow(Registries.LEVEL_STEM); - var old = (Map>, Registry>) field.get(reg); - var fake = new HashMap<>(old); - fake.put(Registries.LEVEL_STEM, injected); - field.set(reg, fake); - - return new AutoClosing(() -> field.set(reg, old)); + String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); + worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); + } + return worlds; } @Override @@ -696,53 +679,74 @@ public class NMSBinding implements INMSBinding { } @Override - public void removeCustomDimensions(World world) { - ((CraftWorld) world).getHandle().K.customDimensions = null; - } - - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { - var access = registry(); - var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE); - - var settings = new FlatLevelGeneratorSettings( - Optional.empty(), - access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), - List.of() - ); - settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); - settings.updateLayers(); - - var source = new FlatLevelSource(settings); - var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental()); - if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD); - if (nether) register(fake, dimensions, source, LevelStem.NETHER); - if (end) register(fake, dimensions, source, LevelStem.END); - copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null)); - - if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM)); - - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); - } - - private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { - var loc = createIrisKey(key); - target.register(key, new LevelStem( - dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), - source - ), Lifecycle.stable()); - } - - private void copy(MappedRegistry target, Registry source) { - if (source == null) return; - source.registryKeySet().forEach(key -> { - var value = source.get(key); - var info = source.lifecycle(value); - if (value != null && info != null && !target.containsKey(key)) - target.register(key, value, info); - }); + public boolean injectBukkit() { + if (injected.getAndSet(true)) + return true; + try { + Iris.info("Injecting Bukkit"); + new ByteBuddy() + .redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( + MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, + ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, + boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), Agent.installed()); + return true; + } catch (Throwable e) { + Iris.error(C.RED + "Failed to inject Bukkit"); + e.printStackTrace(); + } + return false; } private ResourceLocation createIrisKey(ResourceKey key) { return new ResourceLocation("iris", key.location().getPath()); } + + public LevelStem levelStem(RegistryAccess access, World.Environment env) { + if (env == World.Environment.CUSTOM) + env = World.Environment.NORMAL; + return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); + } + + private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { + return access.registryOrThrow(Registries.DIMENSION_TYPE).getHolderOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", switch (env) { + case NORMAL, CUSTOM -> "overworld"; + case NETHER -> "the_nether"; + case THE_END -> "the_end"; + }))); + } + + private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { + var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of()); + settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); + settings.updateLayers(); + return new FlatLevelSource(settings); + } + + private static class ServerLevelAdvice { + @SneakyThrows + @Advice.OnMethodEnter + static void enter( + @Advice.Argument(0) MinecraftServer server, + @Advice.Argument(3) PrimaryLevelData levelData, + @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem, + @Advice.Argument(12) World.Environment env, + @Advice.Argument(value = 13) ChunkGenerator gen + ) { + if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) + return; + + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) + .invoke(bindings, server.registryAccess(), env); + levelData.customDimensions = null; + } + } } diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java index 2d0e08931..18d4c724c 100644 --- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java @@ -1,32 +1,33 @@ package com.volmit.iris.core.nms.v1_20_R3; import java.awt.Color; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Function; import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.Lifecycle; -import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.util.agent.Agent; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.scheduling.J; import lombok.SneakyThrows; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.*; import net.minecraft.core.Registry; import net.minecraft.nbt.*; import net.minecraft.nbt.Tag; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.WorldLoader; import net.minecraft.server.commands.data.BlockDataAccessor; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.tags.TagKey; +import net.minecraft.world.RandomSequences; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.block.Blocks; @@ -36,6 +37,8 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -45,12 +48,11 @@ import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlockState; import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlockStates; import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; -import org.bukkit.craftbukkit.v1_20_R3.entity.CraftDolphin; import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey; -import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; @@ -68,7 +70,6 @@ import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterBiomeInject; -import com.volmit.iris.util.nbt.io.NBTUtil; import com.volmit.iris.util.nbt.mca.NBTWorld; import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; @@ -79,24 +80,22 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; -import sun.misc.Unsafe; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); - private final AtomicCache dataLoadContext = new AtomicCache<>(); + private final AtomicBoolean injected = new AtomicBoolean(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final ReentrantLock dataContextLock = new ReentrantLock(true); + private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -647,44 +646,28 @@ public class NMSBinding implements INMSBinding { } @Override - @SneakyThrows - public AutoClosing injectLevelStems() { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + @SuppressWarnings("all") + public KMap getMainWorlds() { + String levelName = ServerProperties.LEVEL_NAME; + KMap worlds = new KMap<>(); + for (var key : registry().registryOrThrow(Registries.LEVEL_STEM).registryKeySet()) { + World.Environment env = World.Environment.NORMAL; + if (key == LevelStem.NETHER) { + if (!Bukkit.getAllowNether()) + continue; + env = World.Environment.NETHER; + } else if (key == LevelStem.END) { + if (!Bukkit.getAllowEnd()) + continue; + env = World.Environment.THE_END; + } else if (key != LevelStem.OVERWORLD) { + env = World.Environment.CUSTOM; + } - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - ))); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - - @Override - @SneakyThrows - public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { - var reg = registry(); - var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); - field.setAccessible(true); - - var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); - var injected = access.registryOrThrow(Registries.LEVEL_STEM); - var old = (Map>, Registry>) field.get(reg); - var fake = new HashMap<>(old); - fake.put(Registries.LEVEL_STEM, injected); - field.set(reg, fake); - - return new AutoClosing(() -> field.set(reg, old)); + String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); + worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); + } + return worlds; } @Override @@ -697,53 +680,74 @@ public class NMSBinding implements INMSBinding { } @Override - public void removeCustomDimensions(World world) { - ((CraftWorld) world).getHandle().K.customDimensions = null; - } - - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { - var access = registry(); - var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE); - - var settings = new FlatLevelGeneratorSettings( - Optional.empty(), - access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), - List.of() - ); - settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); - settings.updateLayers(); - - var source = new FlatLevelSource(settings); - var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental()); - if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD); - if (nether) register(fake, dimensions, source, LevelStem.NETHER); - if (end) register(fake, dimensions, source, LevelStem.END); - copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null)); - - if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM)); - - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); - } - - private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { - var loc = createIrisKey(key); - target.register(key, new LevelStem( - dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), - source - ), Lifecycle.stable()); - } - - private void copy(MappedRegistry target, Registry source) { - if (source == null) return; - source.registryKeySet().forEach(key -> { - var value = source.get(key); - var info = source.lifecycle(value); - if (value != null && info != null && !target.containsKey(key)) - target.register(key, value, info); - }); + public boolean injectBukkit() { + if (injected.getAndSet(true)) + return true; + try { + Iris.info("Injecting Bukkit"); + new ByteBuddy() + .redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( + MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, + ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, + boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), Agent.installed()); + return true; + } catch (Throwable e) { + Iris.error(C.RED + "Failed to inject Bukkit"); + e.printStackTrace(); + } + return false; } private ResourceLocation createIrisKey(ResourceKey key) { return new ResourceLocation("iris", key.location().getPath()); } + + public LevelStem levelStem(RegistryAccess access, World.Environment env) { + if (env == World.Environment.CUSTOM) + env = World.Environment.NORMAL; + return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); + } + + private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { + return access.registryOrThrow(Registries.DIMENSION_TYPE).getHolderOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", switch (env) { + case NORMAL, CUSTOM -> "overworld"; + case NETHER -> "the_nether"; + case THE_END -> "the_end"; + }))); + } + + private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { + var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of()); + settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); + settings.updateLayers(); + return new FlatLevelSource(settings); + } + + private static class ServerLevelAdvice { + @SneakyThrows + @Advice.OnMethodEnter + static void enter( + @Advice.Argument(0) MinecraftServer server, + @Advice.Argument(3) PrimaryLevelData levelData, + @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem, + @Advice.Argument(12) World.Environment env, + @Advice.Argument(value = 13) ChunkGenerator gen + ) { + if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) + return; + + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) + .invoke(bindings, server.registryAccess(), env); + levelData.customDimensions = null; + } + } } diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java index ec076e7df..1e6b0e465 100644 --- a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java @@ -5,18 +5,22 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Function; import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.Lifecycle; -import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.util.agent.Agent; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; import lombok.SneakyThrows; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.*; import net.minecraft.core.Registry; import net.minecraft.core.component.DataComponents; @@ -31,23 +35,24 @@ import net.minecraft.nbt.ShortTag; import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.WorldLoader; import net.minecraft.server.commands.data.BlockDataAccessor; -import net.minecraft.server.commands.data.DataCommands; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.tags.TagKey; +import net.minecraft.world.RandomSequences; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; -import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -56,15 +61,12 @@ import org.bukkit.craftbukkit.v1_20_R4.CraftServer; import org.bukkit.craftbukkit.v1_20_R4.CraftWorld; import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockState; import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockStates; -import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockType; import org.bukkit.craftbukkit.v1_20_R4.block.data.CraftBlockData; -import org.bukkit.craftbukkit.v1_20_R4.entity.CraftDolphin; -import org.bukkit.craftbukkit.v1_20_R4.generator.CustomChunkGenerator; import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_20_R4.util.CraftNamespacedKey; -import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; @@ -100,11 +102,11 @@ public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); - private final AtomicCache dataLoadContext = new AtomicCache<>(); + private final AtomicBoolean injected = new AtomicBoolean(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final ReentrantLock dataContextLock = new ReentrantLock(true); + private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -672,44 +674,28 @@ public class NMSBinding implements INMSBinding { } @Override - @SneakyThrows - public AutoClosing injectLevelStems() { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + @SuppressWarnings("all") + public KMap getMainWorlds() { + String levelName = ServerProperties.LEVEL_NAME; + KMap worlds = new KMap<>(); + for (var key : registry().registryOrThrow(Registries.LEVEL_STEM).registryKeySet()) { + World.Environment env = World.Environment.NORMAL; + if (key == LevelStem.NETHER) { + if (!Bukkit.getAllowNether()) + continue; + env = World.Environment.NETHER; + } else if (key == LevelStem.END) { + if (!Bukkit.getAllowEnd()) + continue; + env = World.Environment.THE_END; + } else if (key != LevelStem.OVERWORLD) { + env = World.Environment.CUSTOM; + } - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - ))); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - - @Override - @SneakyThrows - public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { - var reg = registry(); - var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); - field.setAccessible(true); - - var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); - var injected = access.registryOrThrow(Registries.LEVEL_STEM); - var old = (Map>, Registry>) field.get(reg); - var fake = new HashMap<>(old); - fake.put(Registries.LEVEL_STEM, injected); - field.set(reg, fake); - - return new AutoClosing(() -> field.set(reg, old)); + String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); + worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); + } + return worlds; } @Override @@ -722,53 +708,74 @@ public class NMSBinding implements INMSBinding { } @Override - public void removeCustomDimensions(World world) { - ((CraftWorld) world).getHandle().K.customDimensions = null; - } - - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { - var access = registry(); - var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE); - - var settings = new FlatLevelGeneratorSettings( - Optional.empty(), - access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), - List.of() - ); - settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); - settings.updateLayers(); - - var source = new FlatLevelSource(settings); - var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental()); - if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD); - if (nether) register(fake, dimensions, source, LevelStem.NETHER); - if (end) register(fake, dimensions, source, LevelStem.END); - copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null)); - - if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM)); - - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); - } - - private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { - var loc = createIrisKey(key); - target.register(key, new LevelStem( - dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), - source - ), RegistrationInfo.BUILT_IN); - } - - private void copy(MappedRegistry target, Registry source) { - if (source == null) return; - source.registryKeySet().forEach(key -> { - var value = source.get(key); - var info = source.registrationInfo(key).orElse(null); - if (value != null && info != null && !target.containsKey(key)) - target.register(key, value, info); - }); + public boolean injectBukkit() { + if (injected.getAndSet(true)) + return true; + try { + Iris.info("Injecting Bukkit"); + new ByteBuddy() + .redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( + MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, + ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, + boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), Agent.installed()); + return true; + } catch (Throwable e) { + Iris.error(C.RED + "Failed to inject Bukkit"); + e.printStackTrace(); + } + return false; } private ResourceLocation createIrisKey(ResourceKey key) { return new ResourceLocation("iris", key.location().getPath()); } + + public LevelStem levelStem(RegistryAccess access, World.Environment env) { + if (env == World.Environment.CUSTOM) + env = World.Environment.NORMAL; + return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); + } + + private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { + return access.registryOrThrow(Registries.DIMENSION_TYPE).getHolderOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", switch (env) { + case NORMAL, CUSTOM -> "overworld"; + case NETHER -> "the_nether"; + case THE_END -> "the_end"; + }))); + } + + private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { + var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of()); + settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); + settings.updateLayers(); + return new FlatLevelSource(settings); + } + + private static class ServerLevelAdvice { + @SneakyThrows + @Advice.OnMethodEnter + static void enter( + @Advice.Argument(0) MinecraftServer server, + @Advice.Argument(3) PrimaryLevelData levelData, + @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem, + @Advice.Argument(12) World.Environment env, + @Advice.Argument(value = 13) ChunkGenerator gen + ) { + if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) + return; + + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) + .invoke(bindings, server.registryAccess(), env); + levelData.customDimensions = null; + } + } } diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java index 7613bf71f..2fb20264f 100644 --- a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java +++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java @@ -1,35 +1,35 @@ package com.volmit.iris.core.nms.v1_21_R1; import java.awt.Color; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Function; import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.Lifecycle; -import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.util.agent.Agent; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.scheduling.J; import lombok.SneakyThrows; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.*; import net.minecraft.core.Registry; import net.minecraft.core.component.DataComponents; import net.minecraft.nbt.*; import net.minecraft.nbt.Tag; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.WorldLoader; import net.minecraft.server.commands.data.BlockDataAccessor; -import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.tags.TagKey; +import net.minecraft.world.RandomSequences; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.Biomes; @@ -42,6 +42,8 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -51,12 +53,11 @@ import org.bukkit.craftbukkit.v1_21_R1.CraftWorld; import org.bukkit.craftbukkit.v1_21_R1.block.CraftBlockState; import org.bukkit.craftbukkit.v1_21_R1.block.CraftBlockStates; import org.bukkit.craftbukkit.v1_21_R1.block.data.CraftBlockData; -import org.bukkit.craftbukkit.v1_21_R1.entity.CraftDolphin; import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_21_R1.util.CraftNamespacedKey; -import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; @@ -74,7 +75,6 @@ import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterBiomeInject; -import com.volmit.iris.util.nbt.io.NBTUtil; import com.volmit.iris.util.nbt.mca.NBTWorld; import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; @@ -85,23 +85,21 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; -import sun.misc.Unsafe; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); - private final AtomicCache dataLoadContext = new AtomicCache<>(); + private final AtomicBoolean injected = new AtomicBoolean(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final ReentrantLock dataContextLock = new ReentrantLock(true); + private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -676,44 +674,28 @@ public class NMSBinding implements INMSBinding { } @Override - @SneakyThrows - public AutoClosing injectLevelStems() { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + @SuppressWarnings("all") + public KMap getMainWorlds() { + String levelName = ServerProperties.LEVEL_NAME; + KMap worlds = new KMap<>(); + for (var key : registry().registryOrThrow(Registries.LEVEL_STEM).registryKeySet()) { + World.Environment env = World.Environment.NORMAL; + if (key == LevelStem.NETHER) { + if (!Bukkit.getAllowNether()) + continue; + env = World.Environment.NETHER; + } else if (key == LevelStem.END) { + if (!Bukkit.getAllowEnd()) + continue; + env = World.Environment.THE_END; + } else if (key != LevelStem.OVERWORLD) { + env = World.Environment.CUSTOM; + } - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - ))); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - - @Override - @SneakyThrows - public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { - var reg = registry(); - var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); - field.setAccessible(true); - - var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); - var injected = access.registryOrThrow(Registries.LEVEL_STEM); - var old = (Map>, Registry>) field.get(reg); - var fake = new HashMap<>(old); - fake.put(Registries.LEVEL_STEM, injected); - field.set(reg, fake); - - return new AutoClosing(() -> field.set(reg, old)); + String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); + worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); + } + return worlds; } @Override @@ -726,53 +708,74 @@ public class NMSBinding implements INMSBinding { } @Override - public void removeCustomDimensions(World world) { - ((CraftWorld) world).getHandle().K.customDimensions = null; - } - - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { - var access = registry(); - var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE); - - var settings = new FlatLevelGeneratorSettings( - Optional.empty(), - access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), - List.of() - ); - settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); - settings.updateLayers(); - - var source = new FlatLevelSource(settings); - var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental()); - if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD); - if (nether) register(fake, dimensions, source, LevelStem.NETHER); - if (end) register(fake, dimensions, source, LevelStem.END); - copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null)); - - if (copy) copy(fake, access.registryOrThrow(Registries.LEVEL_STEM)); - - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); - } - - private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { - var loc = createIrisKey(key); - target.register(key, new LevelStem( - dimensions.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, loc)).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), - source - ), RegistrationInfo.BUILT_IN); - } - - private void copy(MappedRegistry target, Registry source) { - if (source == null) return; - source.registryKeySet().forEach(key -> { - var value = source.get(key); - var info = source.registrationInfo(key).orElse(null); - if (value != null && info != null && !target.containsKey(key)) - target.register(key, value, info); - }); + public boolean injectBukkit() { + if (injected.getAndSet(true)) + return true; + try { + Iris.info("Injecting Bukkit"); + new ByteBuddy() + .redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( + MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, + ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, + boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), Agent.installed()); + return true; + } catch (Throwable e) { + Iris.error(C.RED + "Failed to inject Bukkit"); + e.printStackTrace(); + } + return false; } private ResourceLocation createIrisKey(ResourceKey key) { return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); } + + public LevelStem levelStem(RegistryAccess access, World.Environment env) { + if (env == World.Environment.CUSTOM) + env = World.Environment.NORMAL; + return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); + } + + private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { + return access.registryOrThrow(Registries.DIMENSION_TYPE).getHolderOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, ResourceLocation.fromNamespaceAndPath("iris", switch (env) { + case NORMAL, CUSTOM -> "overworld"; + case NETHER -> "the_nether"; + case THE_END -> "the_end"; + }))); + } + + private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { + var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID), List.of()); + settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); + settings.updateLayers(); + return new FlatLevelSource(settings); + } + + private static class ServerLevelAdvice { + @SneakyThrows + @Advice.OnMethodEnter + static void enter( + @Advice.Argument(0) MinecraftServer server, + @Advice.Argument(3) PrimaryLevelData levelData, + @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem, + @Advice.Argument(12) World.Environment env, + @Advice.Argument(value = 13) ChunkGenerator gen + ) { + if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) + return; + + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) + .invoke(bindings, server.registryAccess(), env); + levelData.customDimensions = null; + } + } } diff --git a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java index 41b75953b..2a4c1315c 100644 --- a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java +++ b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java @@ -5,26 +5,30 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Function; -import com.mojang.serialization.Lifecycle; -import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; -import com.volmit.iris.core.nms.container.Pair; import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.util.agent.Agent; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.scheduling.J; import lombok.SneakyThrows; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.*; import net.minecraft.core.Registry; import net.minecraft.core.component.DataComponents; import net.minecraft.nbt.*; import net.minecraft.nbt.Tag; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.WorldLoader; import net.minecraft.server.commands.data.BlockDataAccessor; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.tags.TagKey; +import net.minecraft.world.RandomSequences; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.Biomes; @@ -37,6 +41,8 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -50,6 +56,7 @@ import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_21_R2.util.CraftNamespacedKey; import org.bukkit.entity.Entity; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; @@ -87,11 +94,11 @@ public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); - private final AtomicCache dataLoadContext = new AtomicCache<>(); + private final AtomicBoolean injected = new AtomicBoolean(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final ReentrantLock dataContextLock = new ReentrantLock(true); + private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -666,44 +673,28 @@ public class NMSBinding implements INMSBinding { } @Override - @SneakyThrows - public AutoClosing injectLevelStems() { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + @SuppressWarnings("all") + public KMap getMainWorlds() { + String levelName = ServerProperties.LEVEL_NAME; + KMap worlds = new KMap<>(); + for (var key : registry().lookupOrThrow(Registries.LEVEL_STEM).registryKeySet()) { + World.Environment env = World.Environment.NORMAL; + if (key == LevelStem.NETHER) { + if (!Bukkit.getAllowNether()) + continue; + env = World.Environment.NETHER; + } else if (key == LevelStem.END) { + if (!Bukkit.getAllowEnd()) + continue; + env = World.Environment.THE_END; + } else if (key != LevelStem.OVERWORLD) { + env = World.Environment.CUSTOM; + } - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - ))); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - - @Override - @SneakyThrows - public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { - var reg = registry(); - var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); - field.setAccessible(true); - - var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); - var injected = access.lookupOrThrow(Registries.LEVEL_STEM); - var old = (Map>, Registry>) field.get(reg); - var fake = new HashMap<>(old); - fake.put(Registries.LEVEL_STEM, injected); - field.set(reg, fake); - - return new AutoClosing(() -> field.set(reg, old)); + String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); + worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); + } + return worlds; } @Override @@ -716,53 +707,74 @@ public class NMSBinding implements INMSBinding { } @Override - public void removeCustomDimensions(World world) { - ((CraftWorld) world).getHandle().L.customDimensions = null; - } - - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { - var access = registry(); - var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE); - - var settings = new FlatLevelGeneratorSettings( - Optional.empty(), - access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), - List.of() - ); - settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); - settings.updateLayers(); - - var source = new FlatLevelSource(settings); - var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental()); - if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD); - if (nether) register(fake, dimensions, source, LevelStem.NETHER); - if (end) register(fake, dimensions, source, LevelStem.END); - copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null)); - - if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM)); - - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); - } - - private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { - var loc = createIrisKey(key); - target.register(key, new LevelStem( - dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), - source - ), RegistrationInfo.BUILT_IN); - } - - private void copy(MappedRegistry target, Registry source) { - if (source == null) return; - source.listElementIds().forEach(key -> { - var value = source.getValue(key); - var info = source.registrationInfo(key).orElse(null); - if (value != null && info != null && !target.containsKey(key)) - target.register(key, value, info); - }); + public boolean injectBukkit() { + if (injected.getAndSet(true)) + return true; + try { + Iris.info("Injecting Bukkit"); + new ByteBuddy() + .redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( + MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, + ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, + boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), Agent.installed()); + return true; + } catch (Throwable e) { + Iris.error(C.RED + "Failed to inject Bukkit"); + e.printStackTrace(); + } + return false; } private ResourceLocation createIrisKey(ResourceKey key) { return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); } + + public LevelStem levelStem(RegistryAccess access, World.Environment env) { + if (env == World.Environment.CUSTOM) + env = World.Environment.NORMAL; + return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); + } + + private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { + return access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, ResourceLocation.fromNamespaceAndPath("iris", switch (env) { + case NORMAL, CUSTOM -> "overworld"; + case NETHER -> "the_nether"; + case THE_END -> "the_end"; + }))); + } + + private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { + var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of()); + settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); + settings.updateLayers(); + return new FlatLevelSource(settings); + } + + private static class ServerLevelAdvice { + @SneakyThrows + @Advice.OnMethodEnter + static void enter( + @Advice.Argument(0) MinecraftServer server, + @Advice.Argument(3) PrimaryLevelData levelData, + @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem, + @Advice.Argument(12) World.Environment env, + @Advice.Argument(value = 13) ChunkGenerator gen + ) { + if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) + return; + + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) + .invoke(bindings, server.registryAccess(), env); + levelData.customDimensions = null; + } + } } diff --git a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java index c6ba0e6dd..adb05e88d 100644 --- a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java +++ b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java @@ -1,28 +1,31 @@ package com.volmit.iris.core.nms.v1_21_R3; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.serialization.Lifecycle; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; -import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; -import com.volmit.iris.core.nms.container.Pair; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.util.agent.Agent; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.format.C; import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterBiomeInject; +import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.nbt.mca.NBTWorld; import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; import it.unimi.dsi.fastutil.objects.Object2IntMap; import lombok.SneakyThrows; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.Registry; import net.minecraft.core.*; import net.minecraft.core.component.DataComponents; @@ -32,10 +35,11 @@ import net.minecraft.nbt.*; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.WorldLoader; import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.tags.TagKey; +import net.minecraft.world.RandomSequences; import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.level.LevelReader; @@ -54,6 +58,8 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -67,6 +73,7 @@ import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_21_R3.util.CraftNamespacedKey; import org.bukkit.entity.Entity; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; @@ -78,19 +85,19 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Function; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); - private final AtomicCache dataLoadContext = new AtomicCache<>(); + private final AtomicBoolean injected = new AtomicBoolean(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final ReentrantLock dataContextLock = new ReentrantLock(true); + private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -665,44 +672,28 @@ public class NMSBinding implements INMSBinding { } @Override - @SneakyThrows - public AutoClosing injectLevelStems() { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + @SuppressWarnings("all") + public KMap getMainWorlds() { + String levelName = ServerProperties.LEVEL_NAME; + KMap worlds = new KMap<>(); + for (var key : registry().lookupOrThrow(Registries.LEVEL_STEM).registryKeySet()) { + World.Environment env = World.Environment.NORMAL; + if (key == LevelStem.NETHER) { + if (!Bukkit.getAllowNether()) + continue; + env = World.Environment.NETHER; + } else if (key == LevelStem.END) { + if (!Bukkit.getAllowEnd()) + continue; + env = World.Environment.THE_END; + } else if (key != LevelStem.OVERWORLD) { + env = World.Environment.CUSTOM; + } - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - ))); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); - } - - @Override - @SneakyThrows - public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { - var reg = registry(); - var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); - field.setAccessible(true); - - var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); - var injected = access.lookupOrThrow(Registries.LEVEL_STEM); - var old = (Map>, Registry>) field.get(reg); - var fake = new HashMap<>(old); - fake.put(Registries.LEVEL_STEM, injected); - field.set(reg, fake); - - return new AutoClosing(() -> field.set(reg, old)); + String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); + worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); + } + return worlds; } @Override @@ -715,53 +706,74 @@ public class NMSBinding implements INMSBinding { } @Override - public void removeCustomDimensions(World world) { - ((CraftWorld) world).getHandle().L.customDimensions = null; - } - - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { - var access = registry(); - var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE); - - var settings = new FlatLevelGeneratorSettings( - Optional.empty(), - access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), - List.of() - ); - settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); - settings.updateLayers(); - - var source = new FlatLevelSource(settings); - var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental()); - if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD); - if (nether) register(fake, dimensions, source, LevelStem.NETHER); - if (end) register(fake, dimensions, source, LevelStem.END); - copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null)); - - if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM)); - - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); - } - - private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { - var loc = createIrisKey(key); - target.register(key, new LevelStem( - dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), - source - ), RegistrationInfo.BUILT_IN); - } - - private void copy(MappedRegistry target, Registry source) { - if (source == null) return; - source.listElementIds().forEach(key -> { - var value = source.getValue(key); - var info = source.registrationInfo(key).orElse(null); - if (value != null && info != null && !target.containsKey(key)) - target.register(key, value, info); - }); + public boolean injectBukkit() { + if (injected.getAndSet(true)) + return true; + try { + Iris.info("Injecting Bukkit"); + new ByteBuddy() + .redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( + MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, + ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, + boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), Agent.installed()); + return true; + } catch (Throwable e) { + Iris.error(C.RED + "Failed to inject Bukkit"); + e.printStackTrace(); + } + return false; } private ResourceLocation createIrisKey(ResourceKey key) { return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); } + + public LevelStem levelStem(RegistryAccess access, World.Environment env) { + if (env == World.Environment.CUSTOM) + env = World.Environment.NORMAL; + return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); + } + + private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { + return access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, ResourceLocation.fromNamespaceAndPath("iris", switch (env) { + case NORMAL, CUSTOM -> "overworld"; + case NETHER -> "the_nether"; + case THE_END -> "the_end"; + }))); + } + + private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { + var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of()); + settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); + settings.updateLayers(); + return new FlatLevelSource(settings); + } + + private static class ServerLevelAdvice { + @SneakyThrows + @Advice.OnMethodEnter + static void enter( + @Advice.Argument(0) MinecraftServer server, + @Advice.Argument(3) PrimaryLevelData levelData, + @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem, + @Advice.Argument(12) World.Environment env, + @Advice.Argument(value = 13) ChunkGenerator gen + ) { + if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) + return; + + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) + .invoke(bindings, server.registryAccess(), env); + levelData.customDimensions = null; + } + } } From 3e2c0fa02512779865b70892a3bf1b5df6d78043 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 31 Mar 2025 16:35:32 +0200 Subject: [PATCH 009/138] Decrease MSPT impact of throwing ender eyes in Iris worlds --- .../volmit/iris/engine/framework/Engine.java | 47 +++++++++---------- .../framework/EngineAssignedWorldManager.java | 45 ------------------ .../core/nms/v1_20_R1/IrisChunkGenerator.java | 6 +++ .../core/nms/v1_20_R2/IrisChunkGenerator.java | 6 +++ .../core/nms/v1_20_R3/IrisChunkGenerator.java | 5 ++ .../core/nms/v1_20_R4/IrisChunkGenerator.java | 6 +++ .../core/nms/v1_21_R1/IrisChunkGenerator.java | 6 +++ .../core/nms/v1_21_R2/IrisChunkGenerator.java | 6 +++ .../core/nms/v1_21_R3/IrisChunkGenerator.java | 8 ++++ 9 files changed, 66 insertions(+), 69 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java index 8d08771fd..fe99a322d 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java @@ -75,6 +75,7 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; import java.awt.Color; import java.util.Arrays; @@ -852,6 +853,25 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat return getBiomeOrMantle(l.getBlockX(), l.getBlockY(), l.getBlockZ()); } + @Nullable + @BlockCoordinates + default Position2 getNearestStronghold(Position2 pos) { + KList p = getDimension().getStrongholds(getSeedManager().getMantle()); + if (p.isEmpty()) return null; + + Position2 pr = null; + double d = Double.MAX_VALUE; + + for (Position2 i : p) { + double dx = i.distance(pos); + if (dx < d) { + d = dx; + pr = i; + } + } + return pr; + } + default void gotoBiome(IrisBiome biome, Player player, boolean teleport) { Set regionKeys = getDimension() .getAllRegions(this).stream() @@ -872,31 +892,10 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat default void gotoJigsaw(IrisJigsawStructure s, Player player, boolean teleport) { if (s.getLoadKey().equals(getDimension().getStronghold())) { - KList p = getDimension().getStrongholds(getSeedManager().getMantle()); - - if (p.isEmpty()) { + Position2 pr = getNearestStronghold(new Position2(player.getLocation().getBlockX(), player.getLocation().getBlockZ())); + if (pr == null) { player.sendMessage(C.GOLD + "No strongholds in world."); - } - - Position2 px = new Position2(player.getLocation().getBlockX(), player.getLocation().getBlockZ()); - Position2 pr = null; - double d = Double.MAX_VALUE; - - Iris.debug("Ps: " + p.size()); - - for (Position2 i : p) { - Iris.debug("- " + i.getX() + " " + i.getZ()); - } - - for (Position2 i : p) { - double dx = i.distance(px); - if (dx < d) { - d = dx; - pr = i; - } - } - - if (pr != null) { + } else { Location ll = new Location(player.getWorld(), pr.getX(), 40, pr.getZ()); J.s(() -> player.teleport(ll)); } diff --git a/core/src/main/java/com/volmit/iris/engine/framework/EngineAssignedWorldManager.java b/core/src/main/java/com/volmit/iris/engine/framework/EngineAssignedWorldManager.java index 5f801033f..e53b35e5c 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/EngineAssignedWorldManager.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/EngineAssignedWorldManager.java @@ -97,51 +97,6 @@ public abstract class EngineAssignedWorldManager extends EngineAssignedComponent } } - @EventHandler - public void onItemUse(PlayerInteractEvent e) { - if (e.getItem() == null || e.getHand() != EquipmentSlot.HAND) { - return; - } - if (e.getAction() == Action.LEFT_CLICK_BLOCK || e.getAction() == Action.LEFT_CLICK_AIR) { - return; - } - if (e.getPlayer().getWorld().equals(getTarget().getWorld().realWorld()) && e.getItem().getType() == Material.ENDER_EYE) { - if (e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.END_PORTAL_FRAME) { - return; - } - - KList positions = getEngine().getDimension().getStrongholds(getEngine().getSeedManager().getMantle()); - if (positions.isEmpty()) { - return; - } - - Position2 playerPos = new Position2(e.getPlayer().getLocation().getBlockX(), e.getPlayer().getLocation().getBlockZ()); - Position2 pr = positions.get(0); - double d = pr.distance(playerPos); - - for (Position2 pos : positions) { - double distance = pos.distance(playerPos); - if (distance < d) { - d = distance; - pr = pos; - } - } - - if (e.getPlayer().getGameMode() != GameMode.CREATIVE) { - if (e.getItem().getAmount() > 1) { - e.getPlayer().getInventory().getItemInMainHand().setAmount(e.getItem().getAmount() - 1); - } else { - e.getPlayer().getInventory().setItemInMainHand(null); - } - } - - EnderSignal eye = e.getPlayer().getWorld().spawn(e.getPlayer().getLocation().clone().add(0, 0.5F, 0), EnderSignal.class); - eye.setTargetLocation(new Location(e.getPlayer().getWorld(), pr.getX(), 40, pr.getZ())); - eye.getWorld().playSound(eye, Sound.ENTITY_ENDER_EYE_LAUNCH, 1, 1); - Iris.debug("ESignal: " + eye.getTargetLocation().getBlockX() + " " + eye.getTargetLocation().getBlockX()); - } - } - @EventHandler public void on(WorldUnloadEvent e) { if (e.getWorld().equals(getTarget().getWorld().realWorld())) { diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/IrisChunkGenerator.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/IrisChunkGenerator.java index 30873ea31..bebaf4f36 100644 --- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/IrisChunkGenerator.java +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/IrisChunkGenerator.java @@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.StructureTags; import net.minecraft.tags.TagKey; import net.minecraft.util.random.WeightedRandomList; import net.minecraft.world.entity.MobCategory; @@ -122,6 +123,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator { @Override public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (holders.size() == 0) return null; + if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) { + var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ())); + return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0)); + } if (engine.getDimension().isDisableExplorerMaps()) return null; diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/IrisChunkGenerator.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/IrisChunkGenerator.java index 0d44523fe..e2dae5081 100644 --- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/IrisChunkGenerator.java +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/IrisChunkGenerator.java @@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.StructureTags; import net.minecraft.tags.TagKey; import net.minecraft.util.random.WeightedRandomList; import net.minecraft.world.entity.MobCategory; @@ -122,6 +123,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator { @Override public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (holders.size() == 0) return null; + if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) { + var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ())); + return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0)); + } if (engine.getDimension().isDisableExplorerMaps()) return null; diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/IrisChunkGenerator.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/IrisChunkGenerator.java index 26826709b..957901b41 100644 --- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/IrisChunkGenerator.java +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/IrisChunkGenerator.java @@ -125,6 +125,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator { @Override public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (holders.size() == 0) return null; + if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) { + var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ())); + return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0)); + } if (engine.getDimension().isDisableExplorerMaps()) return null; diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/IrisChunkGenerator.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/IrisChunkGenerator.java index 537acc9d9..728638cc3 100644 --- a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/IrisChunkGenerator.java +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/IrisChunkGenerator.java @@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.StructureTags; import net.minecraft.tags.TagKey; import net.minecraft.util.random.WeightedRandomList; import net.minecraft.world.entity.MobCategory; @@ -120,6 +121,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator { @Override public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (holders.size() == 0) return null; + if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) { + var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ())); + return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0)); + } if (engine.getDimension().isDisableExplorerMaps()) return null; diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/IrisChunkGenerator.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/IrisChunkGenerator.java index 461446ed2..046c6a283 100644 --- a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/IrisChunkGenerator.java +++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/IrisChunkGenerator.java @@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.StructureTags; import net.minecraft.tags.TagKey; import net.minecraft.util.random.WeightedRandomList; import net.minecraft.world.entity.MobCategory; @@ -121,6 +122,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator { @Override public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (holders.size() == 0) return null; + if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) { + var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ())); + return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0)); + } if (engine.getDimension().isDisableExplorerMaps()) return null; diff --git a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/IrisChunkGenerator.java b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/IrisChunkGenerator.java index 9dc702b05..a2d273393 100644 --- a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/IrisChunkGenerator.java +++ b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/IrisChunkGenerator.java @@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.StructureTags; import net.minecraft.tags.TagKey; import net.minecraft.util.random.WeightedRandomList; import net.minecraft.world.entity.MobCategory; @@ -120,6 +121,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator { @Override public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (holders.size() == 0) return null; + if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) { + var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ())); + return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0)); + } if (engine.getDimension().isDisableExplorerMaps()) return null; diff --git a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/IrisChunkGenerator.java b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/IrisChunkGenerator.java index 653959edf..ef96101ac 100644 --- a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/IrisChunkGenerator.java +++ b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/IrisChunkGenerator.java @@ -20,9 +20,11 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.StructureTags; import net.minecraft.tags.TagKey; import net.minecraft.util.random.WeightedRandomList; import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.item.EnderEyeItem; import net.minecraft.world.level.*; import net.minecraft.world.level.biome.*; import net.minecraft.world.level.chunk.ChunkAccess; @@ -31,6 +33,7 @@ import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.RandomState; import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.BuiltinStructures; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureSet; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; @@ -114,6 +117,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator { @Override public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (holders.size() == 0) return null; + if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) { + var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ())); + return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0)); + } if (engine.getDimension().isDisableExplorerMaps()) return null; From d2a1e5cc1e86fdcd6b1f3a1c936bb061473498e7 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 30 Mar 2025 14:02:54 +0200 Subject: [PATCH 010/138] fix duplicate & redundant purpur recommendations --- core/src/main/java/com/volmit/iris/Iris.java | 21 +++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 07e01c6e4..64fa8d4d3 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -624,12 +624,22 @@ public class Iris extends VolmitPlugin implements Listener { Iris.warn("="); Iris.warn("============================================"); } - if (!instance.getServer().getVersion().contains("Purpur")) { - passed = false; + + try { + Class.forName("io.papermc.paper.configuration.PaperConfigurations"); + } catch (ClassNotFoundException e) { + Iris.info(C.RED + "Iris requires paper or above to function properly.."); + return false; + } + + try { + Class.forName("org.purpurmc.purpur.PurpurConfig"); + } catch (ClassNotFoundException e) { Iris.info("We recommend using Purpur for the best experience with Iris."); Iris.info("Purpur is a fork of Paper that is optimized for performance and stability."); Iris.info("Plugins that work on Spigot / Paper work on Purpur."); Iris.info("You can download it here: https://purpurmc.org"); + return false; } return passed; } @@ -854,13 +864,6 @@ public class Iris extends VolmitPlugin implements Listener { Iris.info("Server type & version: " + C.RED + Bukkit.getVersion()); } else { Iris.info("Server type & version: " + Bukkit.getVersion()); } Iris.info("Java: " + getJava()); - if (!instance.getServer().getVersion().contains("Purpur")) { - if (instance.getServer().getVersion().contains("Spigot") && instance.getServer().getVersion().contains("Bukkit")) { - Iris.info(C.RED + " Iris requires paper or above to function properly.."); - } else { - Iris.info(C.YELLOW + "Purpur is recommended to use with iris."); - } - } if (getHardware.getProcessMemory() < 5999) { Iris.warn("6GB+ Ram is recommended"); Iris.warn("Process Memory: " + getHardware.getProcessMemory() + " MB"); From f99cc61042fc458bcc5cf0d2ed6c1923be5737d4 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Thu, 10 Apr 2025 23:45:59 +0200 Subject: [PATCH 011/138] add setting to use a virtual thread executor instead of MultiBurst for async pregen --- .../com/volmit/iris/core/IrisSettings.java | 1 + .../methods/AsyncPregenMethod.java | 13 +++- .../volmit/iris/util/parallel/MultiBurst.java | 77 ++++++++++++++++++- 3 files changed, 86 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 5381a7e39..52348a3fe 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -135,6 +135,7 @@ public class IrisSettings { @Data public static class IrisSettingsConcurrency { public int parallelism = -1; + public boolean useVirtualThreads = false; } @Data diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java index 8496f924c..a54e457d0 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java @@ -19,6 +19,7 @@ package com.volmit.iris.core.pregenerator.methods; import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.pregenerator.PregenListener; import com.volmit.iris.core.pregenerator.PregeneratorMethod; import com.volmit.iris.core.tools.IrisToolbelt; @@ -33,11 +34,13 @@ import org.bukkit.World; import java.util.ArrayList; import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; public class AsyncPregenMethod implements PregeneratorMethod { private final World world; - private final MultiBurst burst; + private final ExecutorService service; private final Semaphore semaphore; private final Map lastUse; @@ -47,7 +50,9 @@ public class AsyncPregenMethod implements PregeneratorMethod { } this.world = world; - burst = new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY); + service = IrisSettings.get().getConcurrency().isUseVirtualThreads() ? + Executors.newVirtualThreadPerTaskExecutor() : + new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY); semaphore = new Semaphore(256); this.lastUse = new KMap<>(); } @@ -103,7 +108,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { public void close() { semaphore.acquireUninterruptibly(256); unloadAndSaveAllChunks(); - burst.close(); + service.shutdown(); } @Override @@ -129,7 +134,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { } catch (InterruptedException e) { return; } - burst.complete(() -> completeChunk(x, z, listener)); + service.submit(() -> completeChunk(x, z, listener)); } @Override diff --git a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java index cf840572e..6fbea21cd 100644 --- a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java +++ b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java @@ -24,12 +24,14 @@ import com.volmit.iris.core.service.PreservationSVC; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.math.M; import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import org.jetbrains.annotations.NotNull; +import java.util.Collection; import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; -public class MultiBurst { +public class MultiBurst implements ExecutorService { public static final MultiBurst burst = new MultiBurst(); private final AtomicLong last; private final String name; @@ -144,6 +146,79 @@ public class MultiBurst { return getService().submit(o); } + @Override + public void shutdown() { + close(); + } + + @NotNull + @Override + public List shutdownNow() { + close(); + return List.of(); + } + + @Override + public boolean isShutdown() { + return service == null || service.isShutdown(); + } + + @Override + public boolean isTerminated() { + return service == null || service.isTerminated(); + } + + @Override + public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException { + return service == null || service.awaitTermination(timeout, unit); + } + + @Override + public void execute(@NotNull Runnable command) { + getService().execute(command); + } + + @NotNull + @Override + public Future submit(@NotNull Callable task) { + return service.submit(task); + } + + @NotNull + @Override + public Future submit(@NotNull Runnable task, T result) { + return service.submit(task, result); + } + + @NotNull + @Override + public Future submit(@NotNull Runnable task) { + return service.submit(task); + } + + @NotNull + @Override + public List> invokeAll(@NotNull Collection> tasks) throws InterruptedException { + return service.invokeAll(tasks); + } + + @NotNull + @Override + public List> invokeAll(@NotNull Collection> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException { + return service.invokeAll(tasks, timeout, unit); + } + + @NotNull + @Override + public T invokeAny(@NotNull Collection> tasks) throws InterruptedException, ExecutionException { + return service.invokeAny(tasks); + } + + @Override + public T invokeAny(@NotNull Collection> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return service.invokeAny(tasks, timeout, unit); + } + public void close() { if (service != null) { service.shutdown(); From a10a784c3bc818d5d65b9e963285e17495998641 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 11 Apr 2025 17:50:41 +0200 Subject: [PATCH 012/138] add max concurrency setting for pregen --- .../com/volmit/iris/core/IrisSettings.java | 7 +++- .../iris/core/commands/CommandDeveloper.java | 37 +++++++++++++++++++ .../methods/AsyncPregenMethod.java | 4 +- .../volmit/iris/util/parallel/MultiBurst.java | 14 +++---- 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 52348a3fe..48161dadf 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -24,7 +24,6 @@ import com.volmit.iris.util.io.IO; import com.volmit.iris.util.json.JSONException; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.plugin.VolmitSender; -import com.volmit.iris.util.scheduling.ChronoLatch; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -45,6 +44,7 @@ public class IrisSettings { private IrisSettingsStudio studio = new IrisSettingsStudio(); private IrisSettingsPerformance performance = new IrisSettingsPerformance(); private IrisSettingsUpdater updater = new IrisSettingsUpdater(); + private IrisSettingsPregen pregen = new IrisSettingsPregen(); public static int getThreadCount(int c) { return switch (c) { @@ -135,7 +135,12 @@ public class IrisSettings { @Data public static class IrisSettingsConcurrency { public int parallelism = -1; + } + + @Data + public static class IrisSettingsPregen { public boolean useVirtualThreads = false; + public int maxConcurrency = 256; } @Data diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java index 9b4203490..df491da5f 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java @@ -36,6 +36,7 @@ import com.volmit.iris.util.format.Form; import com.volmit.iris.util.io.CountingDataInputStream; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.mantle.TectonicPlate; +import com.volmit.iris.util.math.M; import com.volmit.iris.util.nbt.mca.MCAFile; import com.volmit.iris.util.nbt.mca.MCAUtil; import com.volmit.iris.util.parallel.MultiBurst; @@ -115,6 +116,42 @@ public class CommandDeveloper implements DecreeExecutor { Iris.info("-------------------------"); } + @Decree(description = "Test") + public void dumpThreads() { + try { + File fi = Iris.instance.getDataFile("dump", "td-" + new java.sql.Date(M.ms()) + ".txt"); + FileOutputStream fos = new FileOutputStream(fi); + Map f = Thread.getAllStackTraces(); + PrintWriter pw = new PrintWriter(fos); + + pw.println(Thread.activeCount() + "/" + f.size()); + var run = Runtime.getRuntime(); + pw.println("Memory:"); + pw.println("\tMax: " + run.maxMemory()); + pw.println("\tTotal: " + run.totalMemory()); + pw.println("\tFree: " + run.freeMemory()); + pw.println("\tUsed: " + (run.totalMemory() - run.freeMemory())); + + for (Thread i : f.keySet()) { + pw.println("========================================"); + pw.println("Thread: '" + i.getName() + "' ID: " + i.getId() + " STATUS: " + i.getState().name()); + + for (StackTraceElement j : f.get(i)) { + pw.println(" @ " + j.toString()); + } + + pw.println("========================================"); + pw.println(); + pw.println(); + } + + pw.close(); + Iris.info("DUMPED! See " + fi.getAbsolutePath()); + } catch (Throwable e) { + e.printStackTrace(); + } + } + @Decree(description = "Test") public void benchmarkMantle( @Param(description = "The world to bench", aliases = {"world"}) diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java index a54e457d0..1283cda30 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java @@ -50,10 +50,10 @@ public class AsyncPregenMethod implements PregeneratorMethod { } this.world = world; - service = IrisSettings.get().getConcurrency().isUseVirtualThreads() ? + service = IrisSettings.get().getPregen().isUseVirtualThreads() ? Executors.newVirtualThreadPerTaskExecutor() : new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY); - semaphore = new Semaphore(256); + semaphore = new Semaphore(IrisSettings.get().getPregen().getMaxConcurrency()); this.lastUse = new KMap<>(); } diff --git a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java index 6fbea21cd..c23aee9d3 100644 --- a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java +++ b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java @@ -181,42 +181,42 @@ public class MultiBurst implements ExecutorService { @NotNull @Override public Future submit(@NotNull Callable task) { - return service.submit(task); + return getService().submit(task); } @NotNull @Override public Future submit(@NotNull Runnable task, T result) { - return service.submit(task, result); + return getService().submit(task, result); } @NotNull @Override public Future submit(@NotNull Runnable task) { - return service.submit(task); + return getService().submit(task); } @NotNull @Override public List> invokeAll(@NotNull Collection> tasks) throws InterruptedException { - return service.invokeAll(tasks); + return getService().invokeAll(tasks); } @NotNull @Override public List> invokeAll(@NotNull Collection> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException { - return service.invokeAll(tasks, timeout, unit); + return getService().invokeAll(tasks, timeout, unit); } @NotNull @Override public T invokeAny(@NotNull Collection> tasks) throws InterruptedException, ExecutionException { - return service.invokeAny(tasks); + return getService().invokeAny(tasks); } @Override public T invokeAny(@NotNull Collection> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - return service.invokeAny(tasks, timeout, unit); + return getService().invokeAny(tasks, timeout, unit); } public void close() { From c00dcf205bccfb368a16abab63ddccc2feab217c Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 13 Apr 2025 00:13:26 +0200 Subject: [PATCH 013/138] fix deadlock when closing pregen method while using modified concurrency --- .../iris/core/pregenerator/methods/AsyncPregenMethod.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java index 1283cda30..e2ab1dfc2 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java @@ -42,6 +42,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { private final World world; private final ExecutorService service; private final Semaphore semaphore; + private final int threads; private final Map lastUse; public AsyncPregenMethod(World world, int threads) { @@ -53,7 +54,8 @@ public class AsyncPregenMethod implements PregeneratorMethod { service = IrisSettings.get().getPregen().isUseVirtualThreads() ? Executors.newVirtualThreadPerTaskExecutor() : new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY); - semaphore = new Semaphore(IrisSettings.get().getPregen().getMaxConcurrency()); + this.threads = IrisSettings.get().getPregen().getMaxConcurrency(); + semaphore = new Semaphore(threads); this.lastUse = new KMap<>(); } @@ -106,7 +108,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { @Override public void close() { - semaphore.acquireUninterruptibly(256); + semaphore.acquireUninterruptibly(threads); unloadAndSaveAllChunks(); service.shutdown(); } From 26e2e20840fc2eaefa0fee92e9b8c1113e9a7a5d Mon Sep 17 00:00:00 2001 From: Julian Krings <47589149+CrazyDev05@users.noreply.github.com> Date: Mon, 14 Apr 2025 21:26:39 +0200 Subject: [PATCH 014/138] Feat/pregen cache (#1190) Add cache to pregen to skip already generated chunks faster --- .../com/volmit/iris/core/IrisSettings.java | 1 + .../iris/core/commands/CommandPregen.java | 6 +- .../volmit/iris/core/gui/PregeneratorJob.java | 24 +- .../core/pregenerator/IrisPregenerator.java | 104 ++++++--- .../core/pregenerator/PregenListener.java | 8 +- .../core/pregenerator/cache/PregenCache.java | 70 ++++++ .../pregenerator/cache/PregenCacheImpl.java | 215 ++++++++++++++++++ .../pregenerator/cache/SynchronizedCache.java | 48 ++++ .../methods/CachedPregenMethod.java | 86 +++++++ .../iris/core/service/GlobalCacheSVC.java | 104 +++++++++ .../volmit/iris/core/tools/IrisToolbelt.java | 14 +- .../volmit/iris/util/parallel/MultiBurst.java | 34 +-- 12 files changed, 651 insertions(+), 63 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCache.java create mode 100644 core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java create mode 100644 core/src/main/java/com/volmit/iris/core/pregenerator/cache/SynchronizedCache.java create mode 100644 core/src/main/java/com/volmit/iris/core/pregenerator/methods/CachedPregenMethod.java create mode 100644 core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 48161dadf..cc96233c0 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -130,6 +130,7 @@ public class IrisSettings { public boolean markerEntitySpawningSystem = true; public boolean effectSystem = true; public boolean worldEditWandCUI = true; + public boolean globalPregenCache = true; } @Data diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java b/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java index 6d7bc42a6..2de5cef25 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java @@ -39,7 +39,9 @@ public class CommandPregen implements DecreeExecutor { @Param(description = "The world to pregen", contextual = true) World world, @Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0") - Vector center + Vector center, + @Param(description = "Open the Iris pregen gui", defaultValue = "true") + boolean gui ) { try { if (sender().isPlayer() && access() == null) { @@ -50,7 +52,7 @@ public class CommandPregen implements DecreeExecutor { IrisToolbelt.pregenerate(PregenTask .builder() .center(new Position2(center.getBlockX(), center.getBlockZ())) - .gui(true) + .gui(gui) .radiusX(radius) .radiusZ(radius) .build(), world); diff --git a/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java b/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java index 63b8919db..e12ca46a6 100644 --- a/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java +++ b/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java @@ -40,6 +40,8 @@ import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.image.BufferedImage; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; @@ -64,6 +66,7 @@ public class PregeneratorJob implements PregenListener { private final Position2 max; private final ChronoLatch cl = new ChronoLatch(TimeUnit.MINUTES.toMillis(1)); private final Engine engine; + private final ExecutorService service; private JFrame frame; private PregenRenderer renderer; private int rgc = 0; @@ -96,6 +99,7 @@ public class PregeneratorJob implements PregenListener { }, "Iris Pregenerator"); t.setPriority(Thread.MIN_PRIORITY); t.start(); + service = Executors.newVirtualThreadPerTaskExecutor(); } public static boolean shutdownInstance() { @@ -219,10 +223,10 @@ public class PregeneratorJob implements PregenListener { } @Override - public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method) { + public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached) { info = new String[]{ (paused() ? "PAUSED" : (saving ? "Saving... " : "Generating")) + " " + Form.f(generated) + " of " + Form.f(totalChunks) + " (" + Form.pc(percent, 0) + " Complete)", - "Speed: " + Form.f(chunksPerSecond, 0) + " Chunks/s, " + Form.f(regionsPerMinute, 1) + " Regions/m, " + Form.f(chunksPerMinute, 0) + " Chunks/m", + "Speed: " + (cached ? "Cached " : "") + Form.f(chunksPerSecond, 0) + " Chunks/s, " + Form.f(regionsPerMinute, 1) + " Regions/m, " + Form.f(chunksPerMinute, 0) + " Chunks/m", Form.duration(eta, 2) + " Remaining " + " (" + Form.duration(elapsed, 2) + " Elapsed)", "Generation Method: " + method, "Memory: " + Form.memSize(monitor.getUsedBytes(), 2) + " (" + Form.pc(monitor.getUsagePercent(), 0) + ") Pressure: " + Form.memSize(monitor.getPressure(), 0) + "/s", @@ -240,13 +244,16 @@ public class PregeneratorJob implements PregenListener { } @Override - public void onChunkGenerated(int x, int z) { - if (engine != null) { - draw(x, z, engine.draw((x << 4) + 8, (z << 4) + 8)); - return; - } + public void onChunkGenerated(int x, int z, boolean cached) { + if (renderer == null || frame == null || !frame.isVisible()) return; + service.submit(() -> { + if (engine != null) { + draw(x, z, engine.draw((x << 4) + 8, (z << 4) + 8)); + return; + } - draw(x, z, COLOR_GENERATED); + draw(x, z, COLOR_GENERATED); + }); } @Override @@ -306,6 +313,7 @@ public class PregeneratorJob implements PregenListener { close(); instance = null; whenDone.forEach(Runnable::run); + service.shutdownNow(); } @Override diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java b/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java index 57aa8b0eb..3201bebf4 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java @@ -31,28 +31,33 @@ import com.volmit.iris.util.math.RollingSequence; import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.Looper; +import com.volmit.iris.util.scheduling.PrecisionStopwatch; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; public class IrisPregenerator { + private static final double INVALID = 9223372036854775807d; private final PregenTask task; private final PregeneratorMethod generator; private final PregenListener listener; private final Looper ticker; private final AtomicBoolean paused; private final AtomicBoolean shutdown; + private final RollingSequence cachedPerSecond; private final RollingSequence chunksPerSecond; private final RollingSequence chunksPerMinute; private final RollingSequence regionsPerMinute; private final KList chunksPerSecondHistory; - private static AtomicInteger generated; - private final AtomicInteger generatedLast; - private final AtomicInteger generatedLastMinute; - private static AtomicInteger totalChunks; + private final AtomicLong generated; + private final AtomicLong generatedLast; + private final AtomicLong generatedLastMinute; + private final AtomicLong cached; + private final AtomicLong cachedLast; + private final AtomicLong cachedLastMinute; + private final AtomicLong totalChunks; private final AtomicLong startTime; private final ChronoLatch minuteLatch; private final AtomicReference currentGeneratorMethod; @@ -74,46 +79,71 @@ public class IrisPregenerator { net = new KSet<>(); currentGeneratorMethod = new AtomicReference<>("Void"); minuteLatch = new ChronoLatch(60000, false); + cachedPerSecond = new RollingSequence(5); chunksPerSecond = new RollingSequence(10); chunksPerMinute = new RollingSequence(10); regionsPerMinute = new RollingSequence(10); chunksPerSecondHistory = new KList<>(); - generated = new AtomicInteger(0); - generatedLast = new AtomicInteger(0); - generatedLastMinute = new AtomicInteger(0); - totalChunks = new AtomicInteger(0); + generated = new AtomicLong(0); + generatedLast = new AtomicLong(0); + generatedLastMinute = new AtomicLong(0); + cached = new AtomicLong(); + cachedLast = new AtomicLong(0); + cachedLastMinute = new AtomicLong(0); + totalChunks = new AtomicLong(0); task.iterateAllChunks((_a, _b) -> totalChunks.incrementAndGet()); startTime = new AtomicLong(M.ms()); ticker = new Looper() { @Override protected long loop() { long eta = computeETA(); - int secondGenerated = generated.get() - generatedLast.get(); - generatedLast.set(generated.get()); - chunksPerSecond.put(secondGenerated); - chunksPerSecondHistory.add(secondGenerated); - if (minuteLatch.flip()) { - int minuteGenerated = generated.get() - generatedLastMinute.get(); - generatedLastMinute.set(generated.get()); - chunksPerMinute.put(minuteGenerated); - regionsPerMinute.put((double) minuteGenerated / 1024D); + long secondCached = cached.get() - cachedLast.get(); + cachedLast.set(cached.get()); + cachedPerSecond.put(secondCached); + + long secondGenerated = generated.get() - generatedLast.get() - secondCached; + generatedLast.set(generated.get()); + if (secondCached == 0 || secondGenerated != 0) { + chunksPerSecond.put(secondGenerated); + chunksPerSecondHistory.add((int) secondGenerated); } - listener.onTick(chunksPerSecond.getAverage(), chunksPerMinute.getAverage(), + if (minuteLatch.flip()) { + long minuteCached = cached.get() - cachedLastMinute.get(); + cachedLastMinute.set(cached.get()); + + long minuteGenerated = generated.get() - generatedLastMinute.get() - minuteCached; + generatedLastMinute.set(generated.get()); + if (minuteCached == 0 || minuteGenerated != 0) { + chunksPerMinute.put(minuteGenerated); + regionsPerMinute.put((double) minuteGenerated / 1024D); + } + } + boolean cached = cachedPerSecond.getAverage() != 0; + + listener.onTick( + cached ? cachedPerSecond.getAverage() : chunksPerSecond.getAverage(), + chunksPerMinute.getAverage(), regionsPerMinute.getAverage(), - (double) generated.get() / (double) totalChunks.get(), - generated.get(), totalChunks.get(), - totalChunks.get() - generated.get(), - eta, M.ms() - startTime.get(), currentGeneratorMethod.get()); + (double) generated.get() / (double) totalChunks.get(), generated.get(), + totalChunks.get(), + totalChunks.get() - generated.get(), eta, M.ms() - startTime.get(), currentGeneratorMethod.get(), + cached); if (cl.flip()) { double percentage = ((double) generated.get() / (double) totalChunks.get()) * 100; - if (!IrisPackBenchmarking.benchmarkInProgress) { - Iris.info("Pregen: " + Form.f(generated.get()) + " of " + Form.f(totalChunks.get()) + " (%.0f%%) " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration(eta, 2), percentage); - } else { - Iris.info("Benchmarking: " + Form.f(generated.get()) + " of " + Form.f(totalChunks.get()) + " (%.0f%%) " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration(eta, 2), percentage); - } + + Iris.info("%s: %s of %s (%.0f%%), %s/s ETA: %s", + IrisPackBenchmarking.benchmarkInProgress ? "Benchmarking" : "Pregen", + Form.f(generated.get()), + Form.f(totalChunks.get()), + percentage, + cached ? + "Cached " + Form.f((int) cachedPerSecond.getAverage()) : + Form.f((int) chunksPerSecond.getAverage()), + Form.duration(eta, 2) + ); } return 1000; } @@ -121,12 +151,13 @@ public class IrisPregenerator { } private long computeETA() { - return (long) (totalChunks.get() > 1024 ? // Generated chunks exceed 1/8th of total? + double d = (long) (totalChunks.get() > 1024 ? // Generated chunks exceed 1/8th of total? // If yes, use smooth function (which gets more accurate over time since its less sensitive to outliers) - ((totalChunks.get() - generated.get()) * ((double) (M.ms() - startTime.get()) / (double) generated.get())) : + ((totalChunks.get() - generated.get() - cached.get()) * ((double) (M.ms() - startTime.get()) / ((double) generated.get() - cached.get()))) : // If no, use quick function (which is less accurate over time but responds better to the initial delay) - ((totalChunks.get() - generated.get()) / chunksPerSecond.getAverage()) * 1000 + ((totalChunks.get() - generated.get() - cached.get()) / chunksPerSecond.getAverage()) * 1000 ); + return Double.isFinite(d) && d != INVALID ? (long) d : 0; } @@ -138,8 +169,10 @@ public class IrisPregenerator { init(); ticker.start(); checkRegions(); + var p = PrecisionStopwatch.start(); task.iterateRegions((x, z) -> visitRegion(x, z, true)); task.iterateRegions((x, z) -> visitRegion(x, z, false)); + Iris.info("Pregen took " + Form.duration((long) p.getMilliseconds())); shutdown(); if (!IrisPackBenchmarking.benchmarkInProgress) { Iris.info(C.IRIS + "Pregen stopped."); @@ -234,8 +267,8 @@ public class IrisPregenerator { private PregenListener listenify(PregenListener listener) { return new PregenListener() { @Override - public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method) { - listener.onTick(chunksPerSecond, chunksPerMinute, regionsPerMinute, percent, generated, totalChunks, chunksRemaining, eta, elapsed, method); + public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached) { + listener.onTick(chunksPerSecond, chunksPerMinute, regionsPerMinute, percent, generated, totalChunks, chunksRemaining, eta, elapsed, method, cached); } @Override @@ -244,9 +277,10 @@ public class IrisPregenerator { } @Override - public void onChunkGenerated(int x, int z) { - listener.onChunkGenerated(x, z); + public void onChunkGenerated(int x, int z, boolean c) { + listener.onChunkGenerated(x, z, c); generated.addAndGet(1); + if (c) cached.addAndGet(1); } @Override diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/PregenListener.java b/core/src/main/java/com/volmit/iris/core/pregenerator/PregenListener.java index fb3ab3952..6f5d83194 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/PregenListener.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/PregenListener.java @@ -19,11 +19,15 @@ package com.volmit.iris.core.pregenerator; public interface PregenListener { - void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method); + void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached); void onChunkGenerating(int x, int z); - void onChunkGenerated(int x, int z); + default void onChunkGenerated(int x, int z) { + onChunkGenerated(x, z, false); + } + + void onChunkGenerated(int x, int z, boolean cached); void onRegionGenerated(int x, int z); diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCache.java b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCache.java new file mode 100644 index 000000000..dff3840cd --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCache.java @@ -0,0 +1,70 @@ +package com.volmit.iris.core.pregenerator.cache; + +import com.volmit.iris.util.documentation.ChunkCoordinates; +import com.volmit.iris.util.documentation.RegionCoordinates; + +import java.io.File; + +public interface PregenCache { + default boolean isThreadSafe() { + return false; + } + + @ChunkCoordinates + boolean isChunkCached(int x, int z); + + @RegionCoordinates + boolean isRegionCached(int x, int z); + + @ChunkCoordinates + void cacheChunk(int x, int z); + + @RegionCoordinates + void cacheRegion(int x, int z); + + void write(); + + static PregenCache create(File directory) { + if (directory == null) return EMPTY; + return new PregenCacheImpl(directory); + } + + default PregenCache sync() { + if (isThreadSafe()) return this; + return new SynchronizedCache(this); + } + + PregenCache EMPTY = new PregenCache() { + @Override + public boolean isThreadSafe() { + return true; + } + + @Override + public boolean isChunkCached(int x, int z) { + return false; + } + + @Override + public boolean isRegionCached(int x, int z) { + return false; + } + + @Override + public void cacheChunk(int x, int z) { + + } + + @Override + public void cacheRegion(int x, int z) { + + } + + @Override + public void write() { + + } + }; + + +} diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java new file mode 100644 index 000000000..bf48e06e9 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java @@ -0,0 +1,215 @@ +package com.volmit.iris.core.pregenerator.cache; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.github.benmanes.caffeine.cache.RemovalCause; +import com.volmit.iris.Iris; +import com.volmit.iris.util.data.Varint; +import com.volmit.iris.util.documentation.ChunkCoordinates; +import com.volmit.iris.util.documentation.RegionCoordinates; +import com.volmit.iris.util.parallel.HyperLock; +import lombok.RequiredArgsConstructor; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; +import org.jetbrains.annotations.Nullable; + +import javax.annotation.concurrent.NotThreadSafe; +import java.io.*; +import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; + +@NotThreadSafe +@RequiredArgsConstructor +class PregenCacheImpl implements PregenCache { + private static final int SIZE = 32; + private final File directory; + private final HyperLock hyperLock = new HyperLock(SIZE * 2, true); + private final LoadingCache cache = Caffeine.newBuilder() + .expireAfterAccess(10, TimeUnit.SECONDS) + .maximumSize(SIZE) + .removalListener(this::onRemoval) + .evictionListener(this::onRemoval) + .build(this::load); + + @ChunkCoordinates + public boolean isChunkCached(int x, int z) { + var plate = cache.get(new Pos(x >> 10, z >> 10)); + if (plate == null) return false; + return plate.isCached((x >> 5) & 31, (z >> 5) & 31, r -> r.isCached(x & 31, z & 31)); + } + + @RegionCoordinates + public boolean isRegionCached(int x, int z) { + var plate = cache.get(new Pos(x >> 5, z >> 5)); + if (plate == null) return false; + return plate.isCached(x & 31, z & 31, Region::isCached); + } + + @ChunkCoordinates + public void cacheChunk(int x, int z) { + var plate = cache.get(new Pos(x >> 10, z >> 10)); + plate.cache((x >> 5) & 31, (z >> 5) & 31, r -> r.cache(x & 31, z & 31)); + } + + @RegionCoordinates + public void cacheRegion(int x, int z) { + var plate = cache.get(new Pos(x >> 5, z >> 5)); + plate.cache(x & 31, z & 31, Region::cache); + } + + public void write() { + cache.asMap().values().forEach(this::write); + } + + private Plate load(Pos key) { + hyperLock.lock(key.x, key.z); + try { + File file = fileForPlate(key); + if (!file.exists()) return new Plate(key); + try (var in = new DataInputStream(new LZ4BlockInputStream(new FileInputStream(file)))) { + return new Plate(key, in); + } catch (IOException e){ + Iris.error("Failed to read pregen cache " + file); + e.printStackTrace(); + return new Plate(key); + } + } finally { + hyperLock.unlock(key.x, key.z); + } + } + + private void write(Plate plate) { + hyperLock.lock(plate.pos.x, plate.pos.z); + try { + File file = fileForPlate(plate.pos); + try (var out = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(file)))) { + plate.write(out); + } catch (IOException e) { + Iris.error("Failed to write pregen cache " + file); + e.printStackTrace(); + } + } finally { + hyperLock.unlock(plate.pos.x, plate.pos.z); + } + } + + private void onRemoval(@Nullable Pos key, @Nullable Plate plate, RemovalCause cause) { + if (plate == null) return; + write(plate); + } + + private File fileForPlate(Pos pos) { + if (!directory.exists() && !directory.mkdirs()) + throw new IllegalStateException("Cannot create directory: " + directory.getAbsolutePath()); + return new File(directory, "c." + pos.x + "." + pos.z + ".lz4b"); + } + + private static class Plate { + private final Pos pos; + private short count; + private Region[] regions; + + public Plate(Pos pos) { + this.pos = pos; + count = 0; + regions = new Region[1024]; + } + + public Plate(Pos pos, DataInput in) throws IOException { + this.pos = pos; + count = (short) Varint.readSignedVarInt(in); + if (count == 1024) return; + regions = new Region[1024]; + for (int i = 0; i < 1024; i++) { + if (in.readBoolean()) continue; + regions[i] = new Region(in); + } + } + + public boolean isCached(int x, int z, Predicate predicate) { + if (count == 1024) return true; + Region region = regions[x * 32 + z]; + if (region == null) return false; + return predicate.test(region); + } + + public void cache(int x, int z, Predicate predicate) { + if (count == 1024) return; + Region region = regions[x * 32 + z]; + if (region == null) regions[x * 32 + z] = region = new Region(); + if (predicate.test(region)) count++; + } + + public void write(DataOutput out) throws IOException { + Varint.writeSignedVarInt(count, out); + if (count == 1024) return; + for (Region region : regions) { + out.writeBoolean(region == null); + if (region == null) continue; + region.write(out); + } + } + } + + private static class Region { + private short count; + private long[] words; + + public Region() { + count = 0; + words = new long[64]; + } + + public Region(DataInput in) throws IOException { + count = (short) Varint.readSignedVarInt(in); + if (count == 1024) return; + words = new long[64]; + for (int i = 0; i < 64; i++) { + words[i] = Varint.readUnsignedVarLong(in); + } + } + + public boolean cache() { + if (count == 1024) return false; + count = 1024; + words = null; + return true; + } + + public boolean cache(int x, int z) { + if (count == 1024) return false; + + int i = x * 32 + z; + int w = i >> 6; + long b = 1L << (i & 63); + + var cur = (words[w] & b) != 0; + if (cur) return false; + + if (++count == 1024) { + words = null; + return true; + } else words[w] |= b; + return false; + } + + public boolean isCached() { + return count == 1024; + } + + public boolean isCached(int x, int z) { + int i = x * 32 + z; + return count == 1024 || (words[i >> 6] & 1L << (i & 63)) != 0; + } + + public void write(DataOutput out) throws IOException { + Varint.writeSignedVarInt(count, out); + if (isCached()) return; + for (long word : words) { + Varint.writeUnsignedVarLong(word, out); + } + } + } + + private record Pos(int x, int z) {} +} diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/SynchronizedCache.java b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/SynchronizedCache.java new file mode 100644 index 000000000..52f3b7774 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/SynchronizedCache.java @@ -0,0 +1,48 @@ +package com.volmit.iris.core.pregenerator.cache; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +class SynchronizedCache implements PregenCache { + private final PregenCache cache; + + @Override + public boolean isThreadSafe() { + return true; + } + + @Override + public boolean isChunkCached(int x, int z) { + synchronized (cache) { + return cache.isChunkCached(x, z); + } + } + + @Override + public boolean isRegionCached(int x, int z) { + synchronized (cache) { + return cache.isRegionCached(x, z); + } + } + + @Override + public void cacheChunk(int x, int z) { + synchronized (cache) { + cache.cacheChunk(x, z); + } + } + + @Override + public void cacheRegion(int x, int z) { + synchronized (cache) { + cache.cacheRegion(x, z); + } + } + + @Override + public void write() { + synchronized (cache) { + cache.write(); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/CachedPregenMethod.java b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/CachedPregenMethod.java new file mode 100644 index 000000000..91c4ddb87 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/CachedPregenMethod.java @@ -0,0 +1,86 @@ +package com.volmit.iris.core.pregenerator.methods; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.pregenerator.PregenListener; +import com.volmit.iris.core.pregenerator.PregeneratorMethod; +import com.volmit.iris.core.pregenerator.cache.PregenCache; +import com.volmit.iris.core.service.GlobalCacheSVC; +import com.volmit.iris.util.mantle.Mantle; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class CachedPregenMethod implements PregeneratorMethod { + private final PregeneratorMethod method; + private final PregenCache cache; + + public CachedPregenMethod(PregeneratorMethod method, String worldName) { + this.method = method; + var cache = Iris.service(GlobalCacheSVC.class).get(worldName); + if (cache == null) { + Iris.debug("Could not find existing cache for " + worldName + " creating fallback"); + cache = GlobalCacheSVC.createDefault(worldName); + } + this.cache = cache; + } + + @Override + public void init() { + method.init(); + } + + @Override + public void close() { + method.close(); + cache.write(); + } + + @Override + public void save() { + method.save(); + cache.write(); + } + + @Override + public boolean supportsRegions(int x, int z, PregenListener listener) { + return cache.isRegionCached(x, z) || method.supportsRegions(x, z, listener); + } + + @Override + public String getMethod(int x, int z) { + return method.getMethod(x, z); + } + + @Override + public void generateRegion(int x, int z, PregenListener listener) { + if (cache.isRegionCached(x, z)) { + listener.onRegionGenerated(x, z); + + int rX = x << 5, rZ = z << 5; + for (int cX = 0; cX < 32; cX++) { + for (int cZ = 0; cZ < 32; cZ++) { + listener.onChunkGenerated(rX + cX, rZ + cZ, true); + listener.onChunkCleaned(rX + cX, rZ + cZ); + } + } + return; + } + method.generateRegion(x, z, listener); + cache.cacheRegion(x, z); + } + + @Override + public void generateChunk(int x, int z, PregenListener listener) { + if (cache.isChunkCached(x, z)) { + listener.onChunkGenerated(x, z, true); + listener.onChunkCleaned(x, z); + return; + } + method.generateChunk(x, z, listener); + cache.cacheChunk(x, z); + } + + @Override + public Mantle getMantle() { + return method.getMantle(); + } +} diff --git a/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java b/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java new file mode 100644 index 000000000..32d6af184 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java @@ -0,0 +1,104 @@ +package com.volmit.iris.core.service; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.pregenerator.cache.PregenCache; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.plugin.IrisService; +import lombok.NonNull; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.WorldInitEvent; +import org.bukkit.event.world.WorldUnloadEvent; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.function.Function; + +public class GlobalCacheSVC implements IrisService { + private static final Cache REFERENCE_CACHE = Caffeine.newBuilder().weakValues().build(); + private final KMap globalCache = new KMap<>(); + private transient boolean lastState; + + @Override + public void onEnable() { + lastState = !IrisSettings.get().getWorld().isGlobalPregenCache(); + if (lastState) return; + Bukkit.getWorlds().forEach(this::createCache); + } + + @Override + public void onDisable() { + globalCache.values().forEach(PregenCache::write); + } + + @Nullable + public PregenCache get(@NonNull World world) { + return globalCache.get(world.getName()); + } + + @Nullable + public PregenCache get(@NonNull String world) { + return globalCache.get(world); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(WorldInitEvent event) { + if (isDisabled()) return; + createCache(event.getWorld()); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(WorldUnloadEvent event) { + var cache = globalCache.remove(event.getWorld().getName()); + if (cache == null) return; + cache.write(); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(ChunkLoadEvent event) { + var cache = get(event.getWorld()); + if (cache == null) return; + cache.cacheChunk(event.getChunk().getX(), event.getChunk().getZ()); + } + + private void createCache(World world) { + globalCache.computeIfAbsent(world.getName(), GlobalCacheSVC::createDefault); + } + + private boolean isDisabled() { + boolean conf = IrisSettings.get().getWorld().isGlobalPregenCache(); + if (lastState != conf) + return lastState; + + if (conf) { + Bukkit.getWorlds().forEach(this::createCache); + } else { + globalCache.values().removeIf(cache -> { + cache.write(); + return true; + }); + } + + return lastState = !conf; + } + + + @NonNull + public static PregenCache createCache(@NonNull String worldName, @NonNull Function provider) { + return REFERENCE_CACHE.get(worldName, provider); + } + + @NonNull + public static PregenCache createDefault(@NonNull String worldName) { + return createCache(worldName, GlobalCacheSVC::createDefault0); + } + + private static PregenCache createDefault0(String worldName) { + return PregenCache.create(new File(Bukkit.getWorldContainer(), String.join(File.separator, worldName, "iris", "pregen"))).sync(); + } +} diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java b/core/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java index ad552ee81..15b3dc8d3 100644 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java @@ -24,6 +24,7 @@ import com.volmit.iris.core.gui.PregeneratorJob; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.pregenerator.PregenTask; import com.volmit.iris.core.pregenerator.PregeneratorMethod; +import com.volmit.iris.core.pregenerator.methods.CachedPregenMethod; import com.volmit.iris.core.pregenerator.methods.HybridPregenMethod; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.engine.framework.Engine; @@ -141,7 +142,18 @@ public class IrisToolbelt { * @return the pregenerator job (already started) */ public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine engine) { - return new PregeneratorJob(task, method, engine); + return pregenerate(task, method, engine, true); + } + + /** + * Start a pregenerator task + * + * @param task the scheduled task + * @param method the method to execute the task + * @return the pregenerator job (already started) + */ + public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine engine, boolean cached) { + return new PregeneratorJob(task, cached && engine != null ? new CachedPregenMethod(method, engine.getWorld().name()) : method, engine); } /** diff --git a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java index c23aee9d3..8a71ac48c 100644 --- a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java +++ b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java @@ -221,27 +221,31 @@ public class MultiBurst implements ExecutorService { public void close() { if (service != null) { - service.shutdown(); - PrecisionStopwatch p = PrecisionStopwatch.start(); - try { - while (!service.awaitTermination(1, TimeUnit.SECONDS)) { - Iris.info("Still waiting to shutdown burster..."); - if (p.getMilliseconds() > 7000) { - Iris.warn("Forcing Shutdown..."); + close(service); + } + } - try { - service.shutdownNow(); - } catch (Throwable e) { + public static void close(ExecutorService service) { + service.shutdown(); + PrecisionStopwatch p = PrecisionStopwatch.start(); + try { + while (!service.awaitTermination(1, TimeUnit.SECONDS)) { + Iris.info("Still waiting to shutdown burster..."); + if (p.getMilliseconds() > 7000) { + Iris.warn("Forcing Shutdown..."); - } + try { + service.shutdownNow(); + } catch (Throwable e) { - break; } + + break; } - } catch (Throwable e) { - e.printStackTrace(); - Iris.reportError(e); } + } catch (Throwable e) { + e.printStackTrace(); + Iris.reportError(e); } } } From f1c72974fd47857e28c6aed579537d6bacaad543 Mon Sep 17 00:00:00 2001 From: Pixel <107539181+RePixelatedMC@users.noreply.github.com> Date: Tue, 15 Apr 2025 15:53:50 +0200 Subject: [PATCH 015/138] Cleanup (#1186) * Remove server benchmark command * Moved lazy pregen to dev --- .../iris/core/commands/CommandDeepSearch.java | 134 ---- .../iris/core/commands/CommandDeveloper.java | 1 + .../iris/core/commands/CommandIris.java | 13 - .../iris/core/tools/IrisBenchmarking.java | 625 ------------------ 4 files changed, 1 insertion(+), 772 deletions(-) delete mode 100644 core/src/main/java/com/volmit/iris/core/commands/CommandDeepSearch.java delete mode 100644 core/src/main/java/com/volmit/iris/core/tools/IrisBenchmarking.java diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandDeepSearch.java b/core/src/main/java/com/volmit/iris/core/commands/CommandDeepSearch.java deleted file mode 100644 index d8cd6a260..000000000 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandDeepSearch.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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 com.volmit.iris.core.commands; - -import com.volmit.iris.Iris; -import com.volmit.iris.core.pregenerator.DeepSearchPregenerator; -import com.volmit.iris.core.pregenerator.PregenTask; -import com.volmit.iris.core.pregenerator.TurboPregenerator; -import com.volmit.iris.core.tools.IrisToolbelt; -import com.volmit.iris.util.data.Dimension; -import com.volmit.iris.util.decree.DecreeExecutor; -import com.volmit.iris.util.decree.annotations.Decree; -import com.volmit.iris.util.decree.annotations.Param; -import com.volmit.iris.util.format.C; -import com.volmit.iris.util.math.Position2; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.util.Vector; - -import java.io.File; -import java.io.IOException; - -@Decree(name = "DeepSearch", aliases = "search", description = "Pregenerate your Iris worlds!") -public class CommandDeepSearch implements DecreeExecutor { - public String worldName; - @Decree(description = "DeepSearch a world") - public void start( - @Param(description = "The radius of the pregen in blocks", aliases = "size") - int radius, - @Param(description = "The world to pregen", contextual = true) - World world, - @Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0") - Vector center - ) { - - worldName = world.getName(); - File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName()); - File TurboFile = new File(worldDirectory, "DeepSearch.json"); - if (TurboFile.exists()) { - if (DeepSearchPregenerator.getInstance() != null) { - sender().sendMessage(C.BLUE + "DeepSearch is already in progress"); - Iris.info(C.YELLOW + "DeepSearch is already in progress"); - return; - } else { - try { - TurboFile.delete(); - } catch (Exception e){ - Iris.error("Failed to delete the old instance file of DeepSearch!"); - return; - } - } - } - - try { - if (sender().isPlayer() && access() == null) { - sender().sendMessage(C.RED + "The engine access for this world is null!"); - sender().sendMessage(C.RED + "Please make sure the world is loaded & the engine is initialized. Generate a new chunk, for example."); - } - - DeepSearchPregenerator.DeepSearchJob DeepSearchJob = DeepSearchPregenerator.DeepSearchJob.builder() - .world(world) - .radiusBlocks(radius) - .position(0) - .build(); - - File SearchGenFile = new File(worldDirectory, "DeepSearch.json"); - DeepSearchPregenerator pregenerator = new DeepSearchPregenerator(DeepSearchJob, SearchGenFile); - pregenerator.start(); - - String msg = C.GREEN + "DeepSearch started in " + C.GOLD + worldName + C.GREEN + " of " + C.GOLD + (radius * 2) + C.GREEN + " by " + C.GOLD + (radius * 2) + C.GREEN + " blocks from " + C.GOLD + center.getX() + "," + center.getZ(); - sender().sendMessage(msg); - Iris.info(msg); - } catch (Throwable e) { - sender().sendMessage(C.RED + "Epic fail. See console."); - Iris.reportError(e); - e.printStackTrace(); - } - } - - @Decree(description = "Stop the active DeepSearch task", aliases = "x") - public void stop(@Param(aliases = "world", description = "The world to pause") World world) throws IOException { - DeepSearchPregenerator DeepSearchInstance = DeepSearchPregenerator.getInstance(); - File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName()); - File turboFile = new File(worldDirectory, "DeepSearch.json"); - - if (DeepSearchInstance != null) { - DeepSearchInstance.shutdownInstance(world); - sender().sendMessage(C.LIGHT_PURPLE + "Closed Turbogen instance for " + world.getName()); - } else if (turboFile.exists() && turboFile.delete()) { - sender().sendMessage(C.LIGHT_PURPLE + "Closed Turbogen instance for " + world.getName()); - } else if (turboFile.exists()) { - Iris.error("Failed to delete the old instance file of Turbo Pregen!"); - } else { - sender().sendMessage(C.YELLOW + "No active pregeneration tasks to stop"); - } - } - - @Decree(description = "Pause / continue the active pregeneration task", aliases = {"t", "resume", "unpause"}) - public void pause( - @Param(aliases = "world", description = "The world to pause") - World world - ) { - if (TurboPregenerator.getInstance() != null) { - TurboPregenerator.setPausedTurbo(world); - sender().sendMessage(C.GREEN + "Paused/unpaused Turbo Pregen, now: " + (TurboPregenerator.isPausedTurbo(world) ? "Paused" : "Running") + "."); - } else { - File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName()); - File TurboFile = new File(worldDirectory, "DeepSearch.json"); - if (TurboFile.exists()){ - TurboPregenerator.loadTurboGenerator(world.getName()); - sender().sendMessage(C.YELLOW + "Started DeepSearch back up!"); - } else { - sender().sendMessage(C.YELLOW + "No active DeepSearch tasks to pause/unpause."); - } - - } - } -} diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java index df491da5f..f9590151a 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java @@ -63,6 +63,7 @@ import java.util.zip.GZIPOutputStream; @Decree(name = "Developer", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"dev"}) public class CommandDeveloper implements DecreeExecutor { private CommandTurboPregen turboPregen; + private CommandLazyPregen lazyPregen; private CommandUpdater updater; @Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true) diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java index 9279beb71..46e8719a9 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java @@ -24,7 +24,6 @@ import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.pregenerator.ChunkUpdater; import com.volmit.iris.core.service.StudioSVC; -import com.volmit.iris.core.tools.IrisBenchmarking; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.IrisDimension; @@ -59,7 +58,6 @@ import java.util.List; import static com.volmit.iris.Iris.service; import static com.volmit.iris.core.service.EditSVC.deletingWorld; -import static com.volmit.iris.core.tools.IrisBenchmarking.inProgress; import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode; import static com.volmit.iris.core.safeguard.ServerBootSFG.incompatibilities; import static org.bukkit.Bukkit.getServer; @@ -68,7 +66,6 @@ import static org.bukkit.Bukkit.getServer; public class CommandIris implements DecreeExecutor { private CommandStudio studio; private CommandPregen pregen; - private CommandLazyPregen lazyPregen; private CommandSettings settings; private CommandObject object; private CommandJigsaw jigsaw; @@ -175,16 +172,6 @@ public class CommandIris implements DecreeExecutor { sender().sendMessage(C.GREEN + "Iris v" + Iris.instance.getDescription().getVersion() + " by Volmit Software"); } - //todo Move to React - @Decree(description = "Benchmark your server", origin = DecreeOrigin.CONSOLE) - public void serverbenchmark() throws InterruptedException { - if(!inProgress) { - IrisBenchmarking.runBenchmark(); - } else { - Iris.info(C.RED + "Benchmark already is in progress."); - } - } - /* /todo @Decree(description = "Benchmark a pack", origin = DecreeOrigin.CONSOLE) diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisBenchmarking.java b/core/src/main/java/com/volmit/iris/core/tools/IrisBenchmarking.java deleted file mode 100644 index 79bf1b643..000000000 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisBenchmarking.java +++ /dev/null @@ -1,625 +0,0 @@ -package com.volmit.iris.core.tools; - -import com.volmit.iris.Iris; -import com.volmit.iris.util.format.C; -import oshi.SystemInfo; -import oshi.hardware.CentralProcessor; -import oshi.hardware.GlobalMemory; -import oshi.hardware.HWDiskStore; -import oshi.software.os.OperatingSystem; - -import java.io.*; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryMXBean; -import java.lang.management.MemoryUsage; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Base64; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.IntStream; -import java.util.zip.Deflater; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; - -import static com.google.common.math.LongMath.isPrime; -import static com.volmit.iris.util.misc.getHardware.getCPUModel; -public class IrisBenchmarking { - static String ServerOS; - static String filePath = "benchmark.dat"; - static double avgWriteSpeedMBps; - static double avgReadSpeedMBps; - static double highestWriteSpeedMBps; - static double highestReadSpeedMBps; - static double lowestWriteSpeedMBps; - static double lowestReadSpeedMBps; - static double calculateIntegerMath; - static double calculateFloatingPoint; - static double calculatePrimeNumbers; - static double calculateStringSorting; - static double calculateDataEncryption; - static double calculateDataCompression; - static String currentRunning = "None"; - static int BenchmarksCompleted = 0; - static int BenchmarksTotal = 7; - static int totalTasks = 10; - static int currentTasks = 0; - static double WindowsCPUCompression; - static double WindowsCPUEncryption; - static double WindowsCPUCSHA1; - static double elapsedTimeNs; - static boolean Winsat = false; - static boolean WindowsDiskSpeed = false; - public static boolean inProgress = false; - static double startTime; - // Good enough for now. . . - - public static void runBenchmark() throws InterruptedException { - inProgress = true; - getServerOS(); - deleteTestFile(filePath); - AtomicReference doneCalculateDiskSpeed = new AtomicReference<>((double) 0); - startBenchmarkTimer(); - Iris.info("Benchmark Started!"); - Iris.warn("Although it may seem momentarily paused, it's actively processing."); - BenchmarksCompleted = 0; - - CompletableFuture future = CompletableFuture.runAsync(() -> { - currentRunning = "calculateDiskSpeed"; - progressBar(); - if (ServerOS.contains("Windows") && isRunningAsAdmin()) { - WindowsDiskSpeed = true; - WindowsDiskSpeedTest(); - } else { - warningFallback(); - try { - Thread.sleep(10); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - doneCalculateDiskSpeed.set(roundToTwoDecimalPlaces(calculateDiskSpeed())); - BenchmarksCompleted++; - } - - - }).thenRun(() -> { - currentRunning = "WindowsCpuSpeedTest"; - progressBar(); - if (ServerOS.contains("Windows") && isRunningAsAdmin()) { - Winsat = true; - WindowsCpuSpeedTest(); - } else { - Iris.info("Skipping:" + C.BLUE + " Windows System Assessment Tool Benchmarks"); - if (!ServerOS.contains("Windows")) { - Iris.info("Required Software:" + C.BLUE + " Windows"); - BenchmarksTotal = 6; - } - if (!isRunningAsAdmin()) { - Iris.info(C.RED + "ERROR: " + C.DARK_RED + "Elevated privileges missing"); - BenchmarksTotal = 6; - } - } - - }).thenRun(() -> { - currentRunning = "calculateIntegerMath"; - progressBar(); - calculateIntegerMath = roundToTwoDecimalPlaces(calculateIntegerMath()); - BenchmarksCompleted++; - }).thenRun(() -> { - currentRunning = "calculateFloatingPoint"; - progressBar(); - calculateFloatingPoint = roundToTwoDecimalPlaces(calculateFloatingPoint()); - BenchmarksCompleted++; - }).thenRun(() -> { - currentRunning = "calculateStringSorting"; - progressBar(); - calculateStringSorting = roundToTwoDecimalPlaces(calculateStringSorting()); - BenchmarksCompleted++; - }).thenRun(() -> { - currentRunning = "calculatePrimeNumbers"; - progressBar(); - calculatePrimeNumbers = roundToTwoDecimalPlaces(calculatePrimeNumbers()); - BenchmarksCompleted++; - }).thenRun(() -> { - currentRunning = "calculateDataEncryption"; - progressBar(); - calculateDataEncryption = roundToTwoDecimalPlaces(calculateDataEncryption()); - BenchmarksCompleted++; - }).thenRun(() -> { - currentRunning = "calculateDataCompression"; - progressBar(); - calculateDataCompression = roundToTwoDecimalPlaces(calculateDataCompression()); - BenchmarksCompleted++; - }).thenRun(() -> { - elapsedTimeNs = stopBenchmarkTimer(); - results(); - inProgress = false; - }); - - try { - future.get(); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - } - } - - public static void progressBar() { - Iris.info("-----------------------------------------------------"); - Iris.info("Currently Running: " + C.BLUE + currentRunning); - // Iris.info("Tasks: " + "Current Tasks: " + C.BLUE + currentTasks + C.WHITE + " / " + "Total Tasks: " + C.BLUE + totalTasks); - Iris.info("Benchmarks Completed: " + C.BLUE + BenchmarksCompleted + C.WHITE + " / " + "Total: " + C.BLUE + BenchmarksTotal); - Iris.info("-----------------------------------------------------"); - } - - public static void results() { - - SystemInfo systemInfo = new SystemInfo(); - GlobalMemory globalMemory = systemInfo.getHardware().getMemory(); - long totalMemoryMB = globalMemory.getTotal() / (1024 * 1024); - long availableMemoryMB = globalMemory.getAvailable() / (1024 * 1024); - long totalPageSize = globalMemory.getPageSize() / (1024 * 1024); - long usedMemoryMB = totalMemoryMB - availableMemoryMB; - MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); - - Iris.info("OS: " + ServerOS); - if (!isRunningAsAdmin() || !ServerOS.contains("Windows")) { - Iris.info(C.GOLD + "For the full results use Windows + Admin Rights.."); - } - Iris.info("CPU Model: " + getCPUModel()); - Iris.info("CPU Score: " + "WIP"); - Iris.info("- Integer Math: " + calculateIntegerMath + " MOps/Sec"); - Iris.info("- Floating Point Math: " + calculateFloatingPoint + " MOps/Sec"); - Iris.info("- Find Prime Numbers: " + calculatePrimeNumbers + " Primes/Sec"); - Iris.info("- Random String Sorting: " + calculateStringSorting + " Thousand Strings/Sec"); - Iris.info("- Data Encryption: " + formatDouble(calculateDataEncryption) + " MBytes/Sec"); - Iris.info("- Data Compression: " + formatDouble(calculateDataCompression) + " MBytes/Sec"); - - if (WindowsDiskSpeed) { - //Iris.info("Disk Model: " + getDiskModel()); - Iris.info(C.BLUE + "- Running with Windows System Assessment Tool"); - Iris.info("- Sequential 64.0 Write: " + C.BLUE + formatDouble(avgWriteSpeedMBps) + " Mbps"); - Iris.info("- Sequential 64.0 Read: " + C.BLUE + formatDouble(avgReadSpeedMBps) + " Mbps"); - } else { - // Iris.info("Disk Model: " + getDiskModel()); - Iris.info(C.GREEN + "- Running in Native Mode"); - Iris.info("- Average Write Speed: " + C.GREEN + formatDouble(avgWriteSpeedMBps) + " Mbps"); - Iris.info("- Average Read Speed: " + C.GREEN + formatDouble(avgReadSpeedMBps) + " Mbps"); - Iris.info("- Highest Write Speed: " + formatDouble(highestWriteSpeedMBps) + " Mbps"); - Iris.info("- Highest Read Speed: " + formatDouble(highestReadSpeedMBps) + " Mbps"); - Iris.info("- Lowest Write Speed: " + formatDouble(lowestWriteSpeedMBps) + " Mbps"); - Iris.info("- Lowest Read Speed: " + formatDouble(lowestReadSpeedMBps) + " Mbps"); - } - Iris.info("Ram Usage: "); - Iris.info("- Total Ram: " + totalMemoryMB + " MB"); - Iris.info("- Used Ram: " + usedMemoryMB + " MB"); - Iris.info("- Total Process Ram: " + C.BLUE + getMaxMemoryUsage() + " MB"); - Iris.info("- Total Paging Size: " + totalPageSize + " MB"); - if (Winsat) { - Iris.info(C.BLUE + "Windows System Assessment Tool: "); - Iris.info("- CPU LZW Compression:" + C.BLUE + formatDouble(WindowsCPUCompression) + " MB/s"); - Iris.info("- CPU AES256 Encryption: " + C.BLUE + formatDouble(WindowsCPUEncryption) + " MB/s"); - Iris.info("- CPU SHA1 Hash: " + C.BLUE + formatDouble(WindowsCPUCSHA1) + " MB/s"); - Iris.info("Duration: " + roundToTwoDecimalPlaces(elapsedTimeNs) + " Seconds"); - } - - } - - public static long getMaxMemoryUsage() { - MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); - MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); - MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage(); - long maxHeapMemory = heapMemoryUsage.getMax(); - long maxNonHeapMemory = nonHeapMemoryUsage.getMax(); - long maxMemoryUsageMB = (maxHeapMemory + maxNonHeapMemory) / (1024 * 1024); - return maxMemoryUsageMB; - } - - public static void getServerOS() { - SystemInfo systemInfo = new SystemInfo(); - OperatingSystem os = systemInfo.getOperatingSystem(); - ServerOS = os.toString(); - } - - public static boolean isRunningAsAdmin() { - if (ServerOS.contains("Windows")) { - try { - Process process = Runtime.getRuntime().exec("winsat disk"); - process.waitFor(); - return process.exitValue() == 0; - } catch (IOException | InterruptedException e) { - // Hmm - } - } - return false; - } - - public static void warningFallback() { - Iris.info(C.RED + "Using the " + C.DARK_RED + "FALLBACK" + C.RED + " method due to compatibility issues. "); - Iris.info(C.RED + "Please note that this may result in less accurate results."); - } - - private static String formatDouble(double value) { - return String.format("%.2f", value); - } - - private static void startBenchmarkTimer() { - startTime = System.nanoTime(); - } - - private static double stopBenchmarkTimer() { - long endTime = System.nanoTime(); - return (endTime - startTime) / 1_000_000_000.0; - } - - private static double calculateIntegerMath() { - final int numIterations = 1_000_000_000; - final int numRuns = 30; - double totalMopsPerSec = 0; - - for (int run = 0; run < numRuns; run++) { - long startTime = System.nanoTime(); - int result = 0; - - for (int i = 0; i < numIterations; i++) { - result += i * 2; - result -= i / 2; - result ^= i; - result <<= 1; - result >>= 1; - } - - long endTime = System.nanoTime(); - double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0; - double mopsPerSec = (numIterations / elapsedSeconds) / 1_000_000.0; - - totalMopsPerSec += mopsPerSec; - } - - double averageMopsPerSec = totalMopsPerSec / numRuns; - return averageMopsPerSec; - } - - private static double calculateFloatingPoint() { - long numIterations = 85_000_000; - int numRuns = 30; - double totalMopsPerSec = 0; - for (int run = 0; run < numRuns; run++) { - double result = 0; - long startTime = System.nanoTime(); - - for (int i = 0; i < numIterations; i++) { - result += Math.sqrt(i) * Math.sin(i) / (i + 1); - } - - long endTime = System.nanoTime(); - double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0; - double mopsPerSec = (numIterations / elapsedSeconds) / 1_000_000.0; - - totalMopsPerSec += mopsPerSec; - } - - double averageMopsPerSec = totalMopsPerSec / numRuns; - return averageMopsPerSec; - } - - private static double calculatePrimeNumbers() { - int primeCount; - long numIterations = 1_000_000; - int numRuns = 30; - double totalMopsPerSec = 0; - - for (int run = 0; run < numRuns; run++) { - primeCount = 0; - long startTime = System.nanoTime(); - - for (int num = 2; primeCount < numIterations; num++) { - if (isPrime(num)) { - primeCount++; - } - } - - long endTime = System.nanoTime(); - double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0; - double mopsPerSec = (primeCount / elapsedSeconds) / 1_000_000.0; - - totalMopsPerSec += mopsPerSec; - } - - double averageMopsPerSec = totalMopsPerSec / numRuns; - return averageMopsPerSec; - } - - private static double calculateStringSorting() { - int stringCount = 1_000_000; - int stringLength = 100; - int numRuns = 30; - double totalMopsPerSec = 0; - - for (int run = 0; run < numRuns; run++) { - List randomStrings = generateRandomStrings(stringCount, stringLength); - long startTime = System.nanoTime(); - randomStrings.sort(String::compareTo); - long endTime = System.nanoTime(); - - double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0; - double mopsPerSec = (stringCount / elapsedSeconds) / 1_000.0; - - totalMopsPerSec += mopsPerSec; - } - - double averageMopsPerSec = totalMopsPerSec / numRuns; - return averageMopsPerSec; - } - - public static double calculateDataEncryption() { - int dataSizeMB = 100; - byte[] dataToEncrypt = generateRandomData(dataSizeMB * 1024 * 1024); - int numRuns = 20; - double totalMBytesPerSec = 0; - - for (int run = 0; run < numRuns; run++) { - long startTime = System.nanoTime(); - byte[] encryptedData = performEncryption(dataToEncrypt, 1); - - long endTime = System.nanoTime(); - double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0; - double mbytesPerSec = (dataToEncrypt.length / (1024 * 1024.0)) / elapsedSeconds; - - totalMBytesPerSec += mbytesPerSec; - } - - double averageMBytesPerSec = totalMBytesPerSec / numRuns; - return averageMBytesPerSec; - } - - private static byte[] performEncryption(byte[] data, int numRuns) { - byte[] key = "MyEncryptionKey".getBytes(); - byte[] result = Arrays.copyOf(data, data.length); - for (int run = 0; run < numRuns; run++) { - for (int i = 0; i < result.length; i++) { - result[i] ^= key[i % key.length]; - } - } - return result; - } - - public static double calculateDataCompression() { - int dataSizeMB = 500; - byte[] dataToCompress = generateRandomData(dataSizeMB * 1024 * 1024); - long startTime = System.nanoTime(); - byte[] compressedData = performCompression(dataToCompress); - long endTime = System.nanoTime(); - - double elapsedSeconds = (endTime - startTime) / 1e9; - double mbytesPerSec = (compressedData.length / (1024.0 * 1024.0)) / elapsedSeconds; - - return mbytesPerSec; - } - - private static byte[] performCompression(byte[] data) { - Deflater deflater = new Deflater(); - deflater.setInput(data); - deflater.finish(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); - - byte[] buffer = new byte[1024]; - while (!deflater.finished()) { - int count = deflater.deflate(buffer); - outputStream.write(buffer, 0, count); - } - - deflater.end(); - return outputStream.toByteArray(); - } - - private static List generateRandomStrings(int count, int length) { - SecureRandom random = new SecureRandom(); - List randomStrings = new ArrayList<>(); - - IntStream.range(0, count).forEach(i -> { - byte[] bytes = new byte[length]; - random.nextBytes(bytes); - randomStrings.add(Base64.getEncoder().encodeToString(bytes)); - }); - return randomStrings; - } - - private static byte[] generateRandomData(int size) { - SecureRandom random = new SecureRandom(); - byte[] data = new byte[size]; - random.nextBytes(data); - return data; - } - - private static double roundToTwoDecimalPlaces(double value) { - return Double.parseDouble(String.format("%.2f", value)); - } - - private static double calculateCPUScore(long elapsedTimeNs) { - return 1.0 / (elapsedTimeNs / 1_000_000.0); - } - - public static double calculateDiskSpeed() { - int numRuns = 10; - int fileSizeMB = 1000; - - double[] writeSpeeds = new double[numRuns]; - double[] readSpeeds = new double[numRuns]; - - for (int run = 0; run < numRuns; run++) { - long writeStartTime = System.nanoTime(); - deleteTestFile(filePath); - createTestFile(filePath, fileSizeMB); - long writeEndTime = System.nanoTime(); - - long readStartTime = System.nanoTime(); - readTestFile(filePath); - long readEndTime = System.nanoTime(); - - double writeSpeed = calculateDiskSpeedMBps(fileSizeMB, writeStartTime, writeEndTime); - double readSpeed = calculateDiskSpeedMBps(fileSizeMB, readStartTime, readEndTime); - - writeSpeeds[run] = writeSpeed; - readSpeeds[run] = readSpeed; - - if (run == 0) { - lowestWriteSpeedMBps = writeSpeed; - highestWriteSpeedMBps = writeSpeed; - lowestReadSpeedMBps = readSpeed; - highestReadSpeedMBps = readSpeed; - } else { - if (writeSpeed < lowestWriteSpeedMBps) { - lowestWriteSpeedMBps = writeSpeed; - } - if (writeSpeed > highestWriteSpeedMBps) { - highestWriteSpeedMBps = writeSpeed; - } - if (readSpeed < lowestReadSpeedMBps) { - lowestReadSpeedMBps = readSpeed; - } - if (readSpeed > highestReadSpeedMBps) { - highestReadSpeedMBps = readSpeed; - } - } - } - avgWriteSpeedMBps = calculateAverage(writeSpeeds); - avgReadSpeedMBps = calculateAverage(readSpeeds); - return 2; - } - - public static void createTestFile(String filePath, int fileSizeMB) { - try { - File file = new File(filePath); - byte[] data = new byte[1024 * 1024]; - Arrays.fill(data, (byte) 0); - FileOutputStream fos = new FileOutputStream(file); - for (int i = 0; i < fileSizeMB; i++) { - fos.write(data); - } - fos.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public static void readTestFile(String filePath) { - try { - File file = new File(filePath); - FileInputStream fis = new FileInputStream(file); - byte[] buffer = new byte[1024]; - while (fis.read(buffer) != -1) { - } - fis.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public static void deleteTestFile(String filePath) { - File file = new File(filePath); - file.delete(); - } - - public static double calculateDiskSpeedMBps(int fileSizeMB, long startTime, long endTime) { - double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0; - double writeSpeed = (fileSizeMB / elapsedSeconds); - return writeSpeed; - } - - public static double calculateAverage(double[] values) { - double sum = 0; - for (double value : values) { - sum += value; - } - return sum / values.length; - } - - public static void WindowsDiskSpeedTest() { - try { - String command = "winsat disk"; - Process process = Runtime.getRuntime().exec(command); - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - String line; - - while ((line = reader.readLine()) != null) { - Iris.debug(line); - - if (line.contains("Disk Sequential 64.0 Read")) { - avgReadSpeedMBps = extractSpeed(line); - } else if (line.contains("Disk Sequential 64.0 Write")) { - avgWriteSpeedMBps = extractSpeed(line); - } - } - - process.waitFor(); - process.destroy(); - - Iris.debug("Sequential Read Speed: " + avgReadSpeedMBps + " MB/s"); - Iris.debug("Sequential Write Speed: " + avgWriteSpeedMBps + " MB/s"); - } catch (IOException | InterruptedException e) { - e.printStackTrace(); - } - } - - private static double extractSpeed(String line) { - String[] tokens = line.split("\\s+"); - for (int i = 0; i < tokens.length; i++) { - if (tokens[i].endsWith("MB/s") && i > 0) { - try { - return Double.parseDouble(tokens[i - 1]); - } catch (NumberFormatException e) { - e.printStackTrace(); - } - } - } - return 0.0; - } - - public static void WindowsCpuSpeedTest() { - try { - String command = "winsat cpuformal"; - Process process = Runtime.getRuntime().exec(command); - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - String line; - - while ((line = reader.readLine()) != null) { - Iris.debug(line); - - if (line.contains("CPU AES256 Encryption")) { - WindowsCPUEncryption = extractCpuInfo(line); - } - if (line.contains("CPU LZW Compression")) { - WindowsCPUCompression = extractCpuInfo(line); - } - if (line.contains("CPU SHA1 Hash")) { - WindowsCPUCSHA1 = extractCpuInfo(line); - } - } - process.waitFor(); - process.destroy(); - - Iris.debug("Winsat Encryption: " + WindowsCPUEncryption + " MB/s"); - } catch (IOException | InterruptedException e) { - e.printStackTrace(); - } - } - - private static double extractCpuInfo(String line) { - String[] tokens = line.split("\\s+"); - for (int i = 0; i < tokens.length; i++) { - if (tokens[i].endsWith("MB/s") && i > 0) { - try { - return Double.parseDouble(tokens[i - 1]); - } catch (NumberFormatException e) { - e.printStackTrace(); - } - } - } - return 0.0; - } - -} \ No newline at end of file From 3415e7c7af00449d68b09c5d0716cbe2c96ed096 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 16 Apr 2025 21:52:30 +0200 Subject: [PATCH 016/138] implement complete world height isolation --- .../volmit/iris/core/ServerConfigurator.java | 109 ++++++++++-------- .../iris/core/nms/datapack/IDataFixer.java | 11 +- .../nms/datapack/v1192/DataFixerV1192.java | 4 +- .../iris/engine/object/IrisDimension.java | 79 +++++++++---- .../engine/platform/BukkitChunkGenerator.java | 57 +++++---- .../platform/PlatformChunkGenerator.java | 10 +- .../iris/core/nms/v1_20_R1/NMSBinding.java | 45 ++++---- .../iris/core/nms/v1_20_R2/NMSBinding.java | 45 ++++---- .../iris/core/nms/v1_20_R3/NMSBinding.java | 45 ++++---- .../iris/core/nms/v1_20_R4/NMSBinding.java | 45 ++++---- .../iris/core/nms/v1_21_R1/NMSBinding.java | 47 ++++---- .../iris/core/nms/v1_21_R2/NMSBinding.java | 45 ++++---- .../iris/core/nms/v1_21_R3/NMSBinding.java | 46 ++++---- 13 files changed, 322 insertions(+), 266 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java index ffeae3115..b4d281c09 100644 --- a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java +++ b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java @@ -26,7 +26,6 @@ import com.volmit.iris.core.nms.datapack.IDataFixer; import com.volmit.iris.engine.object.IrisBiome; import com.volmit.iris.engine.object.IrisBiomeCustom; import com.volmit.iris.engine.object.IrisDimension; -import com.volmit.iris.engine.object.IrisRange; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KSet; @@ -34,8 +33,8 @@ import com.volmit.iris.util.format.C; import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.scheduling.J; -import lombok.Data; import lombok.NonNull; +import lombok.SneakyThrows; import org.bukkit.Bukkit; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; @@ -45,12 +44,12 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; -import java.util.Arrays; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicIntegerArray; import java.util.stream.Stream; -import static com.volmit.iris.core.nms.datapack.IDataFixer.Dimension.*; - public class ServerConfigurator { public static void configure() { IrisSettings.IrisSettingsAutoconfiguration s = IrisSettings.get().getAutoConfiguration(); @@ -112,14 +111,16 @@ public class ServerConfigurator { KList folders = getDatapacksFolder(); KMap> biomes = new KMap<>(); - allPacks().flatMap(height::merge) - .parallel() - .forEach(dim -> { - Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath()); - dim.installBiomes(fixer, dim::getLoader, folders, biomes.computeIfAbsent(dim.getLoadKey(), k -> new KSet<>())); - }); + try (Stream stream = allPacks()) { + stream.flatMap(height::merge) + .parallel() + .forEach(dim -> { + Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath()); + dim.installBiomes(fixer, dim::getLoader, folders, biomes.computeIfAbsent(dim.getLoadKey(), k -> new KSet<>())); + dim.installDimensionType(fixer, folders); + }); + } IrisDimension.writeShared(folders, height); - Iris.info("Data Packs Setup!"); if (fullInstall) @@ -127,19 +128,21 @@ public class ServerConfigurator { } private static void verifyDataPacksPost(boolean allowRestarting) { - boolean bad = allPacks() - .map(data -> { - Iris.verbose("Checking Pack: " + data.getDataFolder().getPath()); - var loader = data.getDimensionLoader(); - return loader.loadAll(loader.getPossibleKeys()) - .stream() - .map(ServerConfigurator::verifyDataPackInstalled) - .toList() - .contains(false); - }) - .toList() - .contains(true); - if (!bad) return; + try (Stream stream = allPacks()) { + boolean bad = stream + .map(data -> { + Iris.verbose("Checking Pack: " + data.getDataFolder().getPath()); + var loader = data.getDimensionLoader(); + return loader.loadAll(loader.getPossibleKeys()) + .stream() + .map(ServerConfigurator::verifyDataPackInstalled) + .toList() + .contains(false); + }) + .toList() + .contains(true); + if (!bad) return; + } if (allowRestarting) { @@ -239,20 +242,24 @@ public class ServerConfigurator { return path.substring(worldContainer.length(), path.length() - l); } + @SneakyThrows private static Stream listFiles(File parent) { - var files = parent.listFiles(); - return files == null ? Stream.empty() : Arrays.stream(files); + if (!parent.isDirectory()) return Stream.empty(); + return Files.walk(parent.toPath()).map(Path::toFile); } - @Data public static class DimensionHeight { private final IDataFixer fixer; - private IrisRange overworld = new IrisRange(); - private IrisRange nether = new IrisRange(); - private IrisRange end = new IrisRange(); - private int logicalOverworld = 0; - private int logicalNether = 0; - private int logicalEnd = 0; + private final AtomicIntegerArray[] dimensions = new AtomicIntegerArray[3]; + + public DimensionHeight(IDataFixer fixer) { + this.fixer = fixer; + for (int i = 0; i < 3; i++) { + dimensions[i] = new AtomicIntegerArray(new int[]{ + Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE + }); + } + } public Stream merge(IrisData data) { Iris.verbose("Checking Pack: " + data.getDataFolder().getPath()); @@ -263,25 +270,29 @@ public class ServerConfigurator { } public void merge(IrisDimension dimension) { - overworld.merge(dimension.getDimensionHeight()); - nether.merge(dimension.getDimensionHeight()); - end.merge(dimension.getDimensionHeight()); - - logicalOverworld = Math.max(logicalOverworld, dimension.getLogicalHeight()); - logicalNether = Math.max(logicalNether, dimension.getLogicalHeightNether()); - logicalEnd = Math.max(logicalEnd, dimension.getLogicalHeightEnd()); + AtomicIntegerArray array = dimensions[dimension.getBaseDimension().ordinal()]; + array.updateAndGet(0, min -> Math.min(min, dimension.getMinHeight())); + array.updateAndGet(1, max -> Math.max(max, dimension.getMaxHeight())); + array.updateAndGet(2, logical -> Math.max(logical, dimension.getLogicalHeight())); } - public String overworldType() { - return fixer.createDimension(OVERRWORLD, overworld, logicalOverworld).toString(4); + public String[] jsonStrings() { + var dims = IDataFixer.Dimension.values(); + var arr = new String[3]; + for (int i = 0; i < 3; i++) { + arr[i] = jsonString(dims[i]); + } + return arr; } - public String netherType() { - return fixer.createDimension(NETHER, nether, logicalNether).toString(4); - } - - public String endType() { - return fixer.createDimension(THE_END, end, logicalEnd).toString(4); + public String jsonString(IDataFixer.Dimension dimension) { + var data = dimensions[dimension.ordinal()]; + int minY = data.get(0); + int maxY = data.get(1); + int logicalHeight = data.get(2); + if (minY == Integer.MAX_VALUE || maxY == Integer.MIN_VALUE || Integer.MIN_VALUE == logicalHeight) + return null; + return fixer.createDimension(dimension, minY, maxY, logicalHeight).toString(4); } } } diff --git a/core/src/main/java/com/volmit/iris/core/nms/datapack/IDataFixer.java b/core/src/main/java/com/volmit/iris/core/nms/datapack/IDataFixer.java index 4d972128e..889acddff 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/datapack/IDataFixer.java +++ b/core/src/main/java/com/volmit/iris/core/nms/datapack/IDataFixer.java @@ -1,7 +1,6 @@ package com.volmit.iris.core.nms.datapack; import com.volmit.iris.engine.object.IrisBiomeCustom; -import com.volmit.iris.engine.object.IrisRange; import com.volmit.iris.util.json.JSONObject; public interface IDataFixer { @@ -12,17 +11,17 @@ public interface IDataFixer { JSONObject rawDimension(Dimension dimension); - default JSONObject createDimension(Dimension dimension, IrisRange height, int logicalHeight) { + default JSONObject createDimension(Dimension dimension, int minY, int maxY, int logicalHeight) { JSONObject obj = rawDimension(dimension); - obj.put("min_y", height.getMin()); - obj.put("height", height.getMax() - height.getMin()); + obj.put("min_y", minY); + obj.put("height", maxY - minY); obj.put("logical_height", logicalHeight); return obj; } enum Dimension { - OVERRWORLD, + OVERWORLD, NETHER, - THE_END + END } } diff --git a/core/src/main/java/com/volmit/iris/core/nms/datapack/v1192/DataFixerV1192.java b/core/src/main/java/com/volmit/iris/core/nms/datapack/v1192/DataFixerV1192.java index a0a854868..f2697cf38 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/datapack/v1192/DataFixerV1192.java +++ b/core/src/main/java/com/volmit/iris/core/nms/datapack/v1192/DataFixerV1192.java @@ -7,7 +7,7 @@ import java.util.Map; public class DataFixerV1192 implements IDataFixer { private static final Map DIMENSIONS = Map.of( - Dimension.OVERRWORLD, """ + Dimension.OVERWORLD, """ { "ambient_light": 0.0, "bed_works": true, @@ -48,7 +48,7 @@ public class DataFixerV1192 implements IDataFixer { "respawn_anchor_works": true, "ultrawarm": true }""", - Dimension.THE_END, """ + Dimension.END, """ { "ambient_light": 0.0, "bed_works": false, diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java index f2c5b724c..ba6b07717 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java @@ -25,11 +25,13 @@ import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisRegistrant; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.datapack.IDataFixer; +import com.volmit.iris.core.nms.datapack.IDataFixer.Dimension; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.data.DataProvider; +import com.volmit.iris.util.data.Varint; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.math.Position2; @@ -45,6 +47,8 @@ import org.bukkit.Material; import org.bukkit.World.Environment; import org.bukkit.block.data.BlockData; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; import java.io.File; import java.io.IOException; @@ -74,10 +78,6 @@ public class IrisDimension extends IrisRegistrant { @MaxNumber(2032) @Desc("Maximum height at which players can be teleported to through gameplay.") private int logicalHeight = 256; - @Desc("Maximum height at which players can be teleported to through gameplay.") - private int logicalHeightEnd = 256; - @Desc("Maximum height at which players can be teleported to through gameplay.") - private int logicalHeightNether = 256; @RegistryListResource(IrisJigsawStructure.class) @Desc("If defined, Iris will place the given jigsaw structure where minecraft should place the overworld stronghold.") private String stronghold; @@ -166,10 +166,6 @@ public class IrisDimension extends IrisRegistrant { private int fluidHeight = 63; @Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.") private IrisRange dimensionHeight = new IrisRange(-64, 320); - @Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.") - private IrisRange dimensionHeightEnd = new IrisRange(-64, 320); - @Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.") - private IrisRange dimensionHeightNether = new IrisRange(-64, 320); @RegistryListResource(IrisBiome.class) @Desc("Keep this either undefined or empty. Setting any biome name into this will force iris to only generate the specified biome. Great for testing.") private String focus = ""; @@ -410,6 +406,40 @@ public class IrisDimension extends IrisRegistrant { }); } + public Dimension getBaseDimension() { + return switch (getEnvironment()) { + case NETHER -> Dimension.NETHER; + case THE_END -> Dimension.END; + default -> Dimension.OVERWORLD; + }; + } + + public String getDimensionTypeKey() { + return getDimensionTypeKey(getBaseDimension(), getMinHeight(), getMaxHeight(), getLogicalHeight()); + } + + public void installDimensionType(IDataFixer fixer, KList folders) { + String key = getDimensionTypeKey(); + String json = fixer.createDimension( + getBaseDimension(), + getMinHeight(), + getMaxHeight(), + getLogicalHeight() + ).toString(4); + + Iris.verbose(" Installing Data Pack Dimension Type: \"iris:" + key + '"'); + for (File datapacks : folders) { + File output = new File(datapacks, "iris/data/iris/dimension_type/" + key + ".json"); + output.getParentFile().mkdirs(); + try { + IO.writeAll(output, json); + } catch (IOException e) { + Iris.reportError(e); + e.printStackTrace(); + } + } + } + @Override public String getFolderName() { return "dimensions"; @@ -426,11 +456,12 @@ public class IrisDimension extends IrisRegistrant { } public static void writeShared(KList folders, DimensionHeight height) { - Iris.verbose(" Installing Data Pack Dimension Types: \"iris:overworld\", \"iris:the_nether\", \"iris:the_end\""); + Iris.verbose(" Installing Data Pack Vanilla Dimension Types"); + String[] jsonStrings = height.jsonStrings(); for (File datapacks : folders) { - write(datapacks, "overworld", height.overworldType()); - write(datapacks, "the_nether", height.netherType()); - write(datapacks, "the_end", height.endType()); + write(datapacks, "overworld", jsonStrings[0]); + write(datapacks, "the_nether", jsonStrings[1]); + write(datapacks, "the_end", jsonStrings[2]); } String raw = """ @@ -454,18 +485,24 @@ public class IrisDimension extends IrisRegistrant { } } - private static void write(File datapacks, String type, String json) { - File dimType = new File(datapacks, "iris/data/iris/dimension_type/" + type + ".json"); - File dimTypeVanilla = new File(datapacks, "iris/data/minecraft/dimension_type/" + type + ".json"); - - dimType.getParentFile().mkdirs(); - try { - IO.writeAll(dimType, json); + public static String getDimensionTypeKey(Dimension dimension, int minY, int maxY, int logicalHeight) { + var stream = new ByteArrayOutputStream(13); + try (var dos = new DataOutputStream(stream)) { + dos.writeByte(dimension.ordinal()); + Varint.writeUnsignedVarInt(logicalHeight, dos); + Varint.writeUnsignedVarInt(maxY - minY, dos); + Varint.writeSignedVarInt(minY, dos); } catch (IOException e) { - Iris.reportError(e); - e.printStackTrace(); + throw new RuntimeException("This is impossible", e); } + return IO.encode(stream.toByteArray()).replace("=", ".").toLowerCase(); + } + + private static void write(File datapacks, String type, String json) { + if (json == null) return; + File dimTypeVanilla = new File(datapacks, "iris/data/minecraft/dimension_type/" + type + ".json"); + if (IrisSettings.get().getGeneral().adjustVanillaHeight || dimTypeVanilla.exists()) { dimTypeVanilla.getParentFile().mkdirs(); try { diff --git a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index 4285dd799..db5076e35 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -24,6 +24,7 @@ import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.engine.IrisEngine; +import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.data.chunk.TerrainChunk; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.EngineTarget; @@ -86,6 +87,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun private final boolean studio; private final AtomicInteger a = new AtomicInteger(0); private final CompletableFuture spawnChunks = new CompletableFuture<>(); + private final AtomicCache targetCache = new AtomicCache<>(); private Engine engine; private Looper hotloader; private StudioMode lastMode; @@ -158,37 +160,48 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun } private void setupEngine() { - IrisData data = IrisData.get(dataLocation); - IrisDimension dimension = data.getDimensionLoader().load(dimensionKey); + lastMode = StudioMode.NORMAL; + engine = new IrisEngine(getTarget(), studio); + populators.clear(); + targetCache.reset(); + } - if (dimension == null) { - Iris.error("Oh No! There's no pack in " + data.getDataFolder().getPath() + " or... there's no dimension for the key " + dimensionKey); - IrisDimension test = IrisData.loadAnyDimension(dimensionKey); + @NotNull + @Override + public EngineTarget getTarget() { + if (engine != null) return engine.getTarget(); - if (test != null) { - Iris.warn("Looks like " + dimensionKey + " exists in " + test.getLoadFile().getPath() + " "); - Iris.service(StudioSVC.class).installIntoWorld(Iris.getSender(), dimensionKey, dataLocation.getParentFile().getParentFile()); - Iris.warn("Attempted to install into " + data.getDataFolder().getPath()); - data.dump(); - data.clearLists(); - test = data.getDimensionLoader().load(dimensionKey); + return targetCache.aquire(() -> { + IrisData data = IrisData.get(dataLocation); + IrisDimension dimension = data.getDimensionLoader().load(dimensionKey); + + if (dimension == null) { + Iris.error("Oh No! There's no pack in " + data.getDataFolder().getPath() + " or... there's no dimension for the key " + dimensionKey); + IrisDimension test = IrisData.loadAnyDimension(dimensionKey); if (test != null) { - Iris.success("Woo! Patched the Engine!"); - dimension = test; + Iris.warn("Looks like " + dimensionKey + " exists in " + test.getLoadFile().getPath() + " "); + Iris.service(StudioSVC.class).installIntoWorld(Iris.getSender(), dimensionKey, dataLocation.getParentFile().getParentFile()); + Iris.warn("Attempted to install into " + data.getDataFolder().getPath()); + data.dump(); + data.clearLists(); + test = data.getDimensionLoader().load(dimensionKey); + + if (test != null) { + Iris.success("Woo! Patched the Engine!"); + dimension = test; + } else { + Iris.error("Failed to patch dimension!"); + throw new RuntimeException("Missing Dimension: " + dimensionKey); + } } else { - Iris.error("Failed to patch dimension!"); + Iris.error("Nope, you don't have an installation containing " + dimensionKey + " try downloading it?"); throw new RuntimeException("Missing Dimension: " + dimensionKey); } - } else { - Iris.error("Nope, you don't have an installation containing " + dimensionKey + " try downloading it?"); - throw new RuntimeException("Missing Dimension: " + dimensionKey); } - } - lastMode = StudioMode.NORMAL; - engine = new IrisEngine(new EngineTarget(world, dimension, data), studio); - populators.clear(); + return new EngineTarget(world, dimension, data); + }); } @Override diff --git a/core/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java index e79c3dd6f..687788527 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java @@ -24,21 +24,23 @@ import com.volmit.iris.engine.framework.EngineTarget; import com.volmit.iris.engine.framework.Hotloadable; import com.volmit.iris.util.data.DataProvider; import org.bukkit.World; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; public interface PlatformChunkGenerator extends Hotloadable, DataProvider { + @Nullable Engine getEngine(); @Override default IrisData getData() { - return getEngine().getData(); + return getTarget().getData(); } - default EngineTarget getTarget() { - return getEngine().getTarget(); - } + @NotNull + EngineTarget getTarget(); void injectChunkReplacement(World world, int x, int z, Consumer jobs); diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java index f096e5a78..e723b55b7 100644 --- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java @@ -7,6 +7,7 @@ import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.agent.Agent; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; @@ -22,7 +23,6 @@ import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; import it.unimi.dsi.fastutil.objects.Object2IntMap; -import lombok.SneakyThrows; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; @@ -49,7 +49,6 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; @@ -78,6 +77,7 @@ import org.jetbrains.annotations.NotNull; import java.awt.Color; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; @@ -700,18 +700,13 @@ public class NMSBinding implements INMSBinding { return new ResourceLocation("iris", key.location().getPath()); } - public LevelStem levelStem(RegistryAccess access, World.Environment env) { - if (env == World.Environment.CUSTOM) - env = World.Environment.NORMAL; - return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); - } + public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { + if (!(raw instanceof PlatformChunkGenerator gen)) + throw new IllegalStateException("Generator is not platform chunk generator!"); - private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { - return access.registryOrThrow(Registries.DIMENSION_TYPE).getHolderOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", switch (env) { - case NORMAL, CUSTOM -> "overworld"; - case NETHER -> "the_nether"; - case THE_END -> "the_end"; - }))); + var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey()); + var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey)); + return new LevelStem(dimensionType, chunkGenerator(access)); } private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { @@ -722,7 +717,6 @@ public class NMSBinding implements INMSBinding { } private static class ServerLevelAdvice { - @SneakyThrows @Advice.OnMethodEnter static void enter( @Advice.Argument(0) MinecraftServer server, @@ -734,15 +728,20 @@ public class NMSBinding implements INMSBinding { if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) return; - Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") - .getClass() - .getClassLoader()) - .getDeclaredMethod("get") - .invoke(null); - levelStem = (LevelStem) bindings.getClass() - .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) - .invoke(bindings, server.registryAccess(), env); - levelData.customDimensions = null; + try { + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class) + .invoke(bindings, server.registryAccess(), gen); + + levelData.customDimensions = null; + } catch (Throwable e) { + throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e); + } } } } diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java index dbba77c83..eed8f0bd6 100644 --- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java @@ -2,6 +2,7 @@ package com.volmit.iris.core.nms.v1_20_R2; import java.awt.Color; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; @@ -11,11 +12,11 @@ import java.util.concurrent.atomic.AtomicInteger; import com.mojang.datafixers.util.Pair; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.agent.Agent; import com.volmit.iris.util.format.C; import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.scheduling.J; -import lombok.SneakyThrows; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; @@ -32,7 +33,6 @@ import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; -import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; @@ -704,18 +704,13 @@ public class NMSBinding implements INMSBinding { return new ResourceLocation("iris", key.location().getPath()); } - public LevelStem levelStem(RegistryAccess access, World.Environment env) { - if (env == World.Environment.CUSTOM) - env = World.Environment.NORMAL; - return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); - } + public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { + if (!(raw instanceof PlatformChunkGenerator gen)) + throw new IllegalStateException("Generator is not platform chunk generator!"); - private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { - return access.registryOrThrow(Registries.DIMENSION_TYPE).getHolderOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", switch (env) { - case NORMAL, CUSTOM -> "overworld"; - case NETHER -> "the_nether"; - case THE_END -> "the_end"; - }))); + var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey()); + var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey)); + return new LevelStem(dimensionType, chunkGenerator(access)); } private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { @@ -726,7 +721,6 @@ public class NMSBinding implements INMSBinding { } private static class ServerLevelAdvice { - @SneakyThrows @Advice.OnMethodEnter static void enter( @Advice.Argument(0) MinecraftServer server, @@ -738,15 +732,20 @@ public class NMSBinding implements INMSBinding { if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) return; - Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") - .getClass() - .getClassLoader()) - .getDeclaredMethod("get") - .invoke(null); - levelStem = (LevelStem) bindings.getClass() - .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) - .invoke(bindings, server.registryAccess(), env); - levelData.customDimensions = null; + try { + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class) + .invoke(bindings, server.registryAccess(), gen); + + levelData.customDimensions = null; + } catch (Throwable e) { + throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e); + } } } } diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java index 18d4c724c..58e2f8df9 100644 --- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java @@ -2,6 +2,7 @@ package com.volmit.iris.core.nms.v1_20_R3; import java.awt.Color; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; @@ -11,11 +12,11 @@ import java.util.concurrent.atomic.AtomicInteger; import com.mojang.datafixers.util.Pair; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.agent.Agent; import com.volmit.iris.util.format.C; import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.scheduling.J; -import lombok.SneakyThrows; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; @@ -32,7 +33,6 @@ import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; -import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; @@ -705,18 +705,13 @@ public class NMSBinding implements INMSBinding { return new ResourceLocation("iris", key.location().getPath()); } - public LevelStem levelStem(RegistryAccess access, World.Environment env) { - if (env == World.Environment.CUSTOM) - env = World.Environment.NORMAL; - return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); - } + public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { + if (!(raw instanceof PlatformChunkGenerator gen)) + throw new IllegalStateException("Generator is not platform chunk generator!"); - private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { - return access.registryOrThrow(Registries.DIMENSION_TYPE).getHolderOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", switch (env) { - case NORMAL, CUSTOM -> "overworld"; - case NETHER -> "the_nether"; - case THE_END -> "the_end"; - }))); + var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey()); + var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey)); + return new LevelStem(dimensionType, chunkGenerator(access)); } private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { @@ -727,7 +722,6 @@ public class NMSBinding implements INMSBinding { } private static class ServerLevelAdvice { - @SneakyThrows @Advice.OnMethodEnter static void enter( @Advice.Argument(0) MinecraftServer server, @@ -739,15 +733,20 @@ public class NMSBinding implements INMSBinding { if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) return; - Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") - .getClass() - .getClassLoader()) - .getDeclaredMethod("get") - .invoke(null); - levelStem = (LevelStem) bindings.getClass() - .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) - .invoke(bindings, server.registryAccess(), env); - levelData.customDimensions = null; + try { + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class) + .invoke(bindings, server.registryAccess(), gen); + + levelData.customDimensions = null; + } catch (Throwable e) { + throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e); + } } } } diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java index 1e6b0e465..066995605 100644 --- a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java @@ -2,6 +2,7 @@ package com.volmit.iris.core.nms.v1_20_R4; import java.awt.Color; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; @@ -12,12 +13,12 @@ import java.util.concurrent.atomic.AtomicInteger; import com.mojang.datafixers.util.Pair; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.agent.Agent; import com.volmit.iris.util.format.C; import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; -import lombok.SneakyThrows; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; @@ -46,7 +47,6 @@ import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; @@ -733,18 +733,13 @@ public class NMSBinding implements INMSBinding { return new ResourceLocation("iris", key.location().getPath()); } - public LevelStem levelStem(RegistryAccess access, World.Environment env) { - if (env == World.Environment.CUSTOM) - env = World.Environment.NORMAL; - return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); - } + public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { + if (!(raw instanceof PlatformChunkGenerator gen)) + throw new IllegalStateException("Generator is not platform chunk generator!"); - private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { - return access.registryOrThrow(Registries.DIMENSION_TYPE).getHolderOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", switch (env) { - case NORMAL, CUSTOM -> "overworld"; - case NETHER -> "the_nether"; - case THE_END -> "the_end"; - }))); + var dimensionKey = new ResourceLocation("iris", gen.getTarget().getDimension().getDimensionTypeKey()); + var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey)); + return new LevelStem(dimensionType, chunkGenerator(access)); } private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { @@ -755,7 +750,6 @@ public class NMSBinding implements INMSBinding { } private static class ServerLevelAdvice { - @SneakyThrows @Advice.OnMethodEnter static void enter( @Advice.Argument(0) MinecraftServer server, @@ -767,15 +761,20 @@ public class NMSBinding implements INMSBinding { if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) return; - Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") - .getClass() - .getClassLoader()) - .getDeclaredMethod("get") - .invoke(null); - levelStem = (LevelStem) bindings.getClass() - .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) - .invoke(bindings, server.registryAccess(), env); - levelData.customDimensions = null; + try { + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class) + .invoke(bindings, server.registryAccess(), gen); + + levelData.customDimensions = null; + } catch (Throwable e) { + throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e); + } } } } diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java index 2fb20264f..50a4c4d7a 100644 --- a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java +++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java @@ -2,6 +2,7 @@ package com.volmit.iris.core.nms.v1_21_R1; import java.awt.Color; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; @@ -12,11 +13,11 @@ import java.util.concurrent.atomic.AtomicInteger; import com.mojang.datafixers.util.Pair; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.agent.Agent; import com.volmit.iris.util.format.C; import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.scheduling.J; -import lombok.SneakyThrows; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; @@ -37,7 +38,6 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.WorldGenContext; -import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; @@ -733,18 +733,15 @@ public class NMSBinding implements INMSBinding { return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); } - public LevelStem levelStem(RegistryAccess access, World.Environment env) { - if (env == World.Environment.CUSTOM) - env = World.Environment.NORMAL; - return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); - } - private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { - return access.registryOrThrow(Registries.DIMENSION_TYPE).getHolderOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, ResourceLocation.fromNamespaceAndPath("iris", switch (env) { - case NORMAL, CUSTOM -> "overworld"; - case NETHER -> "the_nether"; - case THE_END -> "the_end"; - }))); + + public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { + if (!(raw instanceof PlatformChunkGenerator gen)) + throw new IllegalStateException("Generator is not platform chunk generator!"); + + var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey()); + var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey)); + return new LevelStem(dimensionType, chunkGenerator(access)); } private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { @@ -755,7 +752,6 @@ public class NMSBinding implements INMSBinding { } private static class ServerLevelAdvice { - @SneakyThrows @Advice.OnMethodEnter static void enter( @Advice.Argument(0) MinecraftServer server, @@ -767,15 +763,20 @@ public class NMSBinding implements INMSBinding { if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) return; - Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") - .getClass() - .getClassLoader()) - .getDeclaredMethod("get") - .invoke(null); - levelStem = (LevelStem) bindings.getClass() - .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) - .invoke(bindings, server.registryAccess(), env); - levelData.customDimensions = null; + try { + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class) + .invoke(bindings, server.registryAccess(), gen); + + levelData.customDimensions = null; + } catch (Throwable e) { + throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e); + } } } } diff --git a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java index 2a4c1315c..b8504cbe8 100644 --- a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java +++ b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java @@ -2,6 +2,7 @@ package com.volmit.iris.core.nms.v1_21_R2; import java.awt.Color; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; @@ -11,11 +12,11 @@ import java.util.concurrent.atomic.AtomicInteger; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.agent.Agent; import com.volmit.iris.util.format.C; import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.scheduling.J; -import lombok.SneakyThrows; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; @@ -36,7 +37,6 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.WorldGenContext; -import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; @@ -732,18 +732,13 @@ public class NMSBinding implements INMSBinding { return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); } - public LevelStem levelStem(RegistryAccess access, World.Environment env) { - if (env == World.Environment.CUSTOM) - env = World.Environment.NORMAL; - return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); - } + public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { + if (!(raw instanceof PlatformChunkGenerator gen)) + throw new IllegalStateException("Generator is not platform chunk generator!"); - private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { - return access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, ResourceLocation.fromNamespaceAndPath("iris", switch (env) { - case NORMAL, CUSTOM -> "overworld"; - case NETHER -> "the_nether"; - case THE_END -> "the_end"; - }))); + var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey()); + var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey)); + return new LevelStem(dimensionType, chunkGenerator(access)); } private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { @@ -754,7 +749,6 @@ public class NMSBinding implements INMSBinding { } private static class ServerLevelAdvice { - @SneakyThrows @Advice.OnMethodEnter static void enter( @Advice.Argument(0) MinecraftServer server, @@ -766,15 +760,20 @@ public class NMSBinding implements INMSBinding { if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) return; - Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") - .getClass() - .getClassLoader()) - .getDeclaredMethod("get") - .invoke(null); - levelStem = (LevelStem) bindings.getClass() - .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) - .invoke(bindings, server.registryAccess(), env); - levelData.customDimensions = null; + try { + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class) + .invoke(bindings, server.registryAccess(), gen); + + levelData.customDimensions = null; + } catch (Throwable e) { + throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e); + } } } } diff --git a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java index adb05e88d..7f5524b3c 100644 --- a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java +++ b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java @@ -7,6 +7,7 @@ import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.agent.Agent; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; @@ -16,13 +17,11 @@ import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterBiomeInject; -import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.nbt.mca.NBTWorld; import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; import it.unimi.dsi.fastutil.objects.Object2IntMap; -import lombok.SneakyThrows; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; @@ -53,7 +52,6 @@ import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.WorldGenContext; -import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; @@ -81,6 +79,7 @@ import org.jetbrains.annotations.NotNull; import java.awt.Color; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; @@ -731,18 +730,13 @@ public class NMSBinding implements INMSBinding { return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); } - public LevelStem levelStem(RegistryAccess access, World.Environment env) { - if (env == World.Environment.CUSTOM) - env = World.Environment.NORMAL; - return stems.computeIfAbsent(env, key -> new LevelStem(dimensionType(access, key), chunkGenerator(access))); - } + public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { + if (!(raw instanceof PlatformChunkGenerator gen)) + throw new IllegalStateException("Generator is not platform chunk generator!"); - private Holder.Reference dimensionType(RegistryAccess access, World.Environment env) { - return access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, ResourceLocation.fromNamespaceAndPath("iris", switch (env) { - case NORMAL, CUSTOM -> "overworld"; - case NETHER -> "the_nether"; - case THE_END -> "the_end"; - }))); + var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey()); + var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey)); + return new LevelStem(dimensionType, chunkGenerator(access)); } private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { @@ -753,7 +747,6 @@ public class NMSBinding implements INMSBinding { } private static class ServerLevelAdvice { - @SneakyThrows @Advice.OnMethodEnter static void enter( @Advice.Argument(0) MinecraftServer server, @@ -765,15 +758,20 @@ public class NMSBinding implements INMSBinding { if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) return; - Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") - .getClass() - .getClassLoader()) - .getDeclaredMethod("get") - .invoke(null); - levelStem = (LevelStem) bindings.getClass() - .getDeclaredMethod("levelStem", RegistryAccess.class, World.Environment.class) - .invoke(bindings, server.registryAccess(), env); - levelData.customDimensions = null; + try { + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class) + .invoke(bindings, server.registryAccess(), gen); + + levelData.customDimensions = null; + } catch (Throwable e) { + throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e); + } } } } From 6577f4a5de02c72b72d18ac56d7a68257ce62545 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 16 Apr 2025 22:02:42 +0200 Subject: [PATCH 017/138] rough safeguard for missing dimension type --- .../com/volmit/iris/core/nms/INMSBinding.java | 16 +-- .../iris/core/nms/v1X/NMSBinding1X.java | 10 +- .../iris/core/safeguard/ServerBootSFG.java | 31 +--- .../iris/core/nms/v1_20_R1/NMSBinding.java | 53 ++----- .../iris/core/nms/v1_20_R2/NMSBinding.java | 132 +++++++----------- .../iris/core/nms/v1_20_R3/NMSBinding.java | 126 +++++++---------- .../iris/core/nms/v1_20_R4/NMSBinding.java | 124 ++++++---------- .../iris/core/nms/v1_21_R1/NMSBinding.java | 126 +++++++---------- .../iris/core/nms/v1_21_R2/NMSBinding.java | 120 +++++++--------- .../iris/core/nms/v1_21_R3/NMSBinding.java | 48 ++----- 10 files changed, 282 insertions(+), 504 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java index 8c57cb625..5cc90a53b 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java @@ -35,6 +35,7 @@ import org.bukkit.entity.EntityType; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; import java.awt.Color; @@ -88,7 +89,7 @@ public interface INMSBinding { MCABiomeContainer newBiomeContainer(int min, int max); default World createWorld(WorldCreator c) { - if (missingDimensionTypes(c.environment())) + if (missingDimensionTypes(c.generator())) throw new IllegalStateException("Missing dimenstion types to create world"); return c.createWorld(); @@ -126,18 +127,7 @@ public interface INMSBinding { KList getStructureKeys(); - KMap getMainWorlds(); - - boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end); - - default boolean missingDimensionTypes(World.Environment env) { - return switch (env) { - case NORMAL -> missingDimensionTypes(true, false, false); - case NETHER -> missingDimensionTypes(false, true, false); - case THE_END -> missingDimensionTypes(false, false, true); - default -> true; - }; - } + boolean missingDimensionTypes(@Nullable ChunkGenerator generator); default boolean injectBukkit() { return true; diff --git a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java index 90e68c549..4b2ef8158 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java +++ b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java @@ -20,9 +20,7 @@ package com.volmit.iris.core.nms.v1X; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; -import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; -import com.volmit.iris.core.nms.container.Pair; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; @@ -39,6 +37,7 @@ import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.structure.Structure; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; import java.awt.Color; import java.util.stream.StreamSupport; @@ -121,12 +120,7 @@ public class NMSBinding1X implements INMSBinding { } @Override - public KMap getMainWorlds() { - return new KMap<>(); - } - - @Override - public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { + public boolean missingDimensionTypes(@Nullable ChunkGenerator generator) { return false; } diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java index 9ee9a4d2e..37a300a6a 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java @@ -4,11 +4,7 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.v1X.NMSBinding1X; import com.volmit.iris.util.agent.Agent; -import com.volmit.iris.util.misc.ServerProperties; import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import javax.tools.JavaCompiler; @@ -20,7 +16,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.*; -import java.util.function.Predicate; import static com.volmit.iris.Iris.getJavaVersion; import static com.volmit.iris.core.safeguard.IrisSafeguard.*; @@ -189,30 +184,6 @@ public class ServerBootSFG { } private static boolean missingDimensionTypes() { - var irisWorlds = irisWorlds(); - if (irisWorlds.isEmpty()) return false; - - var worlds = INMS.get().getMainWorlds(); - worlds.keySet().removeIf(Predicate.not(irisWorlds::contains)); - - boolean overworld = worlds.containsValue(World.Environment.NORMAL) || worlds.containsValue(World.Environment.CUSTOM); - boolean nether = worlds.containsValue(World.Environment.NETHER); - boolean end = worlds.containsValue(World.Environment.THE_END); - - if (overworld || nether || end) - return INMS.get().missingDimensionTypes(overworld, nether, end); - return false; + return INMS.get().missingDimensionTypes(null); } - - private static List irisWorlds() { - var config = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML); - ConfigurationSection section = config.getConfigurationSection("worlds"); - if (section == null) return List.of(); - - return section.getKeys(false) - .stream() - .filter(k -> section.getString(k + ".generator", "").startsWith("Iris")) - .toList(); - } - } diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java index e723b55b7..54ec3cb4d 100644 --- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java @@ -17,7 +17,6 @@ import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterBiomeInject; -import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.nbt.mca.NBTWorld; import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; @@ -26,8 +25,10 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; -import net.minecraft.core.*; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.*; import net.minecraft.nbt.Tag; @@ -58,7 +59,6 @@ import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; - import org.bukkit.craftbukkit.v1_20_R1.CraftChunk; import org.bukkit.craftbukkit.v1_20_R1.CraftServer; import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; @@ -75,12 +75,14 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import java.awt.*; import java.awt.Color; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; +import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -641,37 +643,16 @@ public class NMSBinding implements INMSBinding { } @Override - @SuppressWarnings("all") - public KMap getMainWorlds() { - String levelName = ServerProperties.LEVEL_NAME; - KMap worlds = new KMap<>(); - for (var key : registry().registryOrThrow(Registries.LEVEL_STEM).registryKeySet()) { - World.Environment env = World.Environment.NORMAL; - if (key == LevelStem.NETHER) { - if (!Bukkit.getAllowNether()) - continue; - env = World.Environment.NETHER; - } else if (key == LevelStem.END) { - if (!Bukkit.getAllowEnd()) - continue; - env = World.Environment.THE_END; - } else if (key != LevelStem.OVERWORLD) { - env = World.Environment.CUSTOM; - } - - String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); - worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); - } - return worlds; - } - - @Override - public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { - var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE); - if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD)); - if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); - if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); - return overworld || nether || end; + public boolean missingDimensionTypes(ChunkGenerator generator) { + if (generator == null) + return registry().registryOrThrow(Registries.DIMENSION_TYPE) + .keySet() + .stream() + .noneMatch(loc -> loc.getNamespace().equals("iris")); + if (!(generator instanceof PlatformChunkGenerator pcg)) + return false; + var dimensionKey = new ResourceLocation("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); + return !registry().registryOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); } @Override @@ -696,10 +677,6 @@ public class NMSBinding implements INMSBinding { return false; } - private ResourceLocation createIrisKey(ResourceKey key) { - return new ResourceLocation("iris", key.location().getPath()); - } - public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { if (!(raw instanceof PlatformChunkGenerator gen)) throw new IllegalStateException("Generator is not platform chunk generator!"); diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java index eed8f0bd6..f21238b97 100644 --- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java @@ -1,38 +1,56 @@ package com.volmit.iris.core.nms.v1_20_R2; -import java.awt.Color; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.*; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - +import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.datafixers.util.Pair; +import com.volmit.iris.Iris; +import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.agent.Agent; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.format.C; -import com.volmit.iris.util.misc.ServerProperties; +import com.volmit.iris.util.hunk.Hunk; +import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; +import com.volmit.iris.util.matter.MatterBiomeInject; +import com.volmit.iris.util.nbt.mca.NBTWorld; +import com.volmit.iris.util.nbt.mca.palette.*; +import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; -import net.minecraft.core.*; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; import net.minecraft.nbt.*; import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.commands.data.BlockDataAccessor; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.tags.TagKey; import net.minecraft.world.RandomSequences; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; @@ -58,34 +76,17 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.volmit.iris.Iris; -import com.volmit.iris.core.nms.INMSBinding; -import com.volmit.iris.engine.data.cache.AtomicCache; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.collection.KMap; -import com.volmit.iris.util.hunk.Hunk; -import com.volmit.iris.util.json.JSONObject; -import com.volmit.iris.util.mantle.Mantle; -import com.volmit.iris.util.math.Vector3d; -import com.volmit.iris.util.matter.MatterBiomeInject; -import com.volmit.iris.util.nbt.mca.NBTWorld; -import com.volmit.iris.util.nbt.mca.palette.*; -import com.volmit.iris.util.nbt.tag.CompoundTag; - -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkStatus; -import net.minecraft.world.level.chunk.LevelChunk; +import java.awt.*; +import java.awt.Color; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); @@ -623,6 +624,19 @@ public class NMSBinding implements INMSBinding { return keys; } + @Override + public boolean missingDimensionTypes(ChunkGenerator generator) { + if (generator == null) + return registry().registryOrThrow(Registries.DIMENSION_TYPE) + .keySet() + .stream() + .noneMatch(loc -> loc.getNamespace().equals("iris")); + if (!(generator instanceof PlatformChunkGenerator pcg)) + return false; + var dimensionKey = new ResourceLocation("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); + return !registry().registryOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); + } + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { try { for (Field f : clazz.getDeclaredFields()) { @@ -644,40 +658,6 @@ public class NMSBinding implements INMSBinding { return registry.getHolderOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey()))); } - @Override - @SuppressWarnings("all") - public KMap getMainWorlds() { - String levelName = ServerProperties.LEVEL_NAME; - KMap worlds = new KMap<>(); - for (var key : registry().registryOrThrow(Registries.LEVEL_STEM).registryKeySet()) { - World.Environment env = World.Environment.NORMAL; - if (key == LevelStem.NETHER) { - if (!Bukkit.getAllowNether()) - continue; - env = World.Environment.NETHER; - } else if (key == LevelStem.END) { - if (!Bukkit.getAllowEnd()) - continue; - env = World.Environment.THE_END; - } else if (key != LevelStem.OVERWORLD) { - env = World.Environment.CUSTOM; - } - - String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); - worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); - } - return worlds; - } - - @Override - public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { - var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE); - if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD)); - if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); - if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); - return overworld || nether || end; - } - @Override public boolean injectBukkit() { if (injected.getAndSet(true)) @@ -700,10 +680,6 @@ public class NMSBinding implements INMSBinding { return false; } - private ResourceLocation createIrisKey(ResourceKey key) { - return new ResourceLocation("iris", key.location().getPath()); - } - public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { if (!(raw instanceof PlatformChunkGenerator gen)) throw new IllegalStateException("Generator is not platform chunk generator!"); diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java index 58e2f8df9..f9b960626 100644 --- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java @@ -1,38 +1,56 @@ package com.volmit.iris.core.nms.v1_20_R3; -import java.awt.Color; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.*; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - +import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.datafixers.util.Pair; +import com.volmit.iris.Iris; +import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.agent.Agent; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.format.C; -import com.volmit.iris.util.misc.ServerProperties; +import com.volmit.iris.util.hunk.Hunk; +import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; +import com.volmit.iris.util.matter.MatterBiomeInject; +import com.volmit.iris.util.nbt.mca.NBTWorld; +import com.volmit.iris.util.nbt.mca.palette.*; +import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; -import net.minecraft.core.*; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; import net.minecraft.nbt.*; import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.commands.data.BlockDataAccessor; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.tags.TagKey; import net.minecraft.world.RandomSequences; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; @@ -58,34 +76,17 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.volmit.iris.Iris; -import com.volmit.iris.core.nms.INMSBinding; -import com.volmit.iris.engine.data.cache.AtomicCache; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.collection.KMap; -import com.volmit.iris.util.hunk.Hunk; -import com.volmit.iris.util.json.JSONObject; -import com.volmit.iris.util.mantle.Mantle; -import com.volmit.iris.util.math.Vector3d; -import com.volmit.iris.util.matter.MatterBiomeInject; -import com.volmit.iris.util.nbt.mca.NBTWorld; -import com.volmit.iris.util.nbt.mca.palette.*; -import com.volmit.iris.util.nbt.tag.CompoundTag; - -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkStatus; -import net.minecraft.world.level.chunk.LevelChunk; +import java.awt.*; +import java.awt.Color; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); @@ -646,37 +647,16 @@ public class NMSBinding implements INMSBinding { } @Override - @SuppressWarnings("all") - public KMap getMainWorlds() { - String levelName = ServerProperties.LEVEL_NAME; - KMap worlds = new KMap<>(); - for (var key : registry().registryOrThrow(Registries.LEVEL_STEM).registryKeySet()) { - World.Environment env = World.Environment.NORMAL; - if (key == LevelStem.NETHER) { - if (!Bukkit.getAllowNether()) - continue; - env = World.Environment.NETHER; - } else if (key == LevelStem.END) { - if (!Bukkit.getAllowEnd()) - continue; - env = World.Environment.THE_END; - } else if (key != LevelStem.OVERWORLD) { - env = World.Environment.CUSTOM; - } - - String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); - worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); - } - return worlds; - } - - @Override - public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { - var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE); - if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD)); - if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); - if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); - return overworld || nether || end; + public boolean missingDimensionTypes(ChunkGenerator generator) { + if (generator == null) + return registry().registryOrThrow(Registries.DIMENSION_TYPE) + .keySet() + .stream() + .noneMatch(loc -> loc.getNamespace().equals("iris")); + if (!(generator instanceof PlatformChunkGenerator pcg)) + return false; + var dimensionKey = new ResourceLocation("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); + return !registry().registryOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); } @Override @@ -701,10 +681,6 @@ public class NMSBinding implements INMSBinding { return false; } - private ResourceLocation createIrisKey(ResourceKey key) { - return new ResourceLocation("iris", key.location().getPath()); - } - public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { if (!(raw instanceof PlatformChunkGenerator gen)) throw new IllegalStateException("Generator is not platform chunk generator!"); diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java index 066995605..7fe71001d 100644 --- a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java @@ -1,51 +1,56 @@ package com.volmit.iris.core.nms.v1_20_R4; -import java.awt.Color; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.*; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - +import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.datafixers.util.Pair; +import com.volmit.iris.Iris; +import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.agent.Agent; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.format.C; -import com.volmit.iris.util.misc.ServerProperties; +import com.volmit.iris.util.hunk.Hunk; +import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; +import com.volmit.iris.util.matter.MatterBiomeInject; +import com.volmit.iris.util.nbt.mca.NBTWorld; +import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.*; import net.minecraft.core.Registry; import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; import net.minecraft.nbt.*; -import net.minecraft.nbt.ByteTag; -import net.minecraft.nbt.DoubleTag; -import net.minecraft.nbt.EndTag; -import net.minecraft.nbt.FloatTag; -import net.minecraft.nbt.IntTag; -import net.minecraft.nbt.LongTag; -import net.minecraft.nbt.ShortTag; -import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.tags.TagKey; import net.minecraft.world.RandomSequences; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; @@ -72,31 +77,17 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.volmit.iris.Iris; -import com.volmit.iris.core.nms.INMSBinding; -import com.volmit.iris.engine.data.cache.AtomicCache; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.collection.KMap; -import com.volmit.iris.util.hunk.Hunk; -import com.volmit.iris.util.json.JSONObject; -import com.volmit.iris.util.mantle.Mantle; -import com.volmit.iris.util.math.Vector3d; -import com.volmit.iris.util.matter.MatterBiomeInject; -import com.volmit.iris.util.nbt.mca.NBTWorld; -import com.volmit.iris.util.nbt.mca.palette.*; - -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.LevelChunk; +import java.awt.*; +import java.awt.Color; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); @@ -674,37 +665,16 @@ public class NMSBinding implements INMSBinding { } @Override - @SuppressWarnings("all") - public KMap getMainWorlds() { - String levelName = ServerProperties.LEVEL_NAME; - KMap worlds = new KMap<>(); - for (var key : registry().registryOrThrow(Registries.LEVEL_STEM).registryKeySet()) { - World.Environment env = World.Environment.NORMAL; - if (key == LevelStem.NETHER) { - if (!Bukkit.getAllowNether()) - continue; - env = World.Environment.NETHER; - } else if (key == LevelStem.END) { - if (!Bukkit.getAllowEnd()) - continue; - env = World.Environment.THE_END; - } else if (key != LevelStem.OVERWORLD) { - env = World.Environment.CUSTOM; - } - - String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); - worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); - } - return worlds; - } - - @Override - public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { - var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE); - if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD)); - if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); - if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); - return overworld || nether || end; + public boolean missingDimensionTypes(ChunkGenerator generator) { + if (generator == null) + return registry().registryOrThrow(Registries.DIMENSION_TYPE) + .keySet() + .stream() + .noneMatch(loc -> loc.getNamespace().equals("iris")); + if (!(generator instanceof PlatformChunkGenerator pcg)) + return false; + var dimensionKey = new ResourceLocation("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); + return !registry().registryOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); } @Override @@ -729,10 +699,6 @@ public class NMSBinding implements INMSBinding { return false; } - private ResourceLocation createIrisKey(ResourceKey key) { - return new ResourceLocation("iris", key.location().getPath()); - } - public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { if (!(raw instanceof PlatformChunkGenerator gen)) throw new IllegalStateException("Generator is not platform chunk generator!"); diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java index 50a4c4d7a..18b7daac9 100644 --- a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java +++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java @@ -1,41 +1,58 @@ package com.volmit.iris.core.nms.v1_21_R1; -import java.awt.Color; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.*; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - +import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.datafixers.util.Pair; +import com.volmit.iris.Iris; +import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.agent.Agent; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.format.C; -import com.volmit.iris.util.misc.ServerProperties; +import com.volmit.iris.util.hunk.Hunk; +import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; +import com.volmit.iris.util.matter.MatterBiomeInject; +import com.volmit.iris.util.nbt.mca.NBTWorld; +import com.volmit.iris.util.nbt.mca.palette.*; +import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; -import net.minecraft.core.*; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; import net.minecraft.nbt.*; import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.commands.data.BlockDataAccessor; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.tags.TagKey; import net.minecraft.world.RandomSequences; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.WorldGenContext; import net.minecraft.world.level.dimension.LevelStem; @@ -63,33 +80,17 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.volmit.iris.Iris; -import com.volmit.iris.core.nms.INMSBinding; -import com.volmit.iris.engine.data.cache.AtomicCache; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.collection.KMap; -import com.volmit.iris.util.hunk.Hunk; -import com.volmit.iris.util.json.JSONObject; -import com.volmit.iris.util.mantle.Mantle; -import com.volmit.iris.util.math.Vector3d; -import com.volmit.iris.util.matter.MatterBiomeInject; -import com.volmit.iris.util.nbt.mca.NBTWorld; -import com.volmit.iris.util.nbt.mca.palette.*; -import com.volmit.iris.util.nbt.tag.CompoundTag; - -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.LevelChunk; +import java.awt.*; +import java.awt.Color; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); @@ -674,37 +675,16 @@ public class NMSBinding implements INMSBinding { } @Override - @SuppressWarnings("all") - public KMap getMainWorlds() { - String levelName = ServerProperties.LEVEL_NAME; - KMap worlds = new KMap<>(); - for (var key : registry().registryOrThrow(Registries.LEVEL_STEM).registryKeySet()) { - World.Environment env = World.Environment.NORMAL; - if (key == LevelStem.NETHER) { - if (!Bukkit.getAllowNether()) - continue; - env = World.Environment.NETHER; - } else if (key == LevelStem.END) { - if (!Bukkit.getAllowEnd()) - continue; - env = World.Environment.THE_END; - } else if (key != LevelStem.OVERWORLD) { - env = World.Environment.CUSTOM; - } - - String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); - worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); - } - return worlds; - } - - @Override - public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { - var registry = registry().registryOrThrow(Registries.DIMENSION_TYPE); - if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD)); - if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); - if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); - return overworld || nether || end; + public boolean missingDimensionTypes(ChunkGenerator generator) { + if (generator == null) + return registry().registryOrThrow(Registries.DIMENSION_TYPE) + .keySet() + .stream() + .noneMatch(loc -> loc.getNamespace().equals("iris")); + if (!(generator instanceof PlatformChunkGenerator pcg)) + return false; + var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); + return !registry().registryOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); } @Override @@ -729,12 +709,6 @@ public class NMSBinding implements INMSBinding { return false; } - private ResourceLocation createIrisKey(ResourceKey key) { - return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); - } - - - public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { if (!(raw instanceof PlatformChunkGenerator gen)) throw new IllegalStateException("Generator is not platform chunk generator!"); diff --git a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java index b8504cbe8..7b0fb012b 100644 --- a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java +++ b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java @@ -1,40 +1,55 @@ package com.volmit.iris.core.nms.v1_21_R2; -import java.awt.Color; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.*; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.volmit.iris.Iris; +import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.agent.Agent; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.format.C; -import com.volmit.iris.util.misc.ServerProperties; +import com.volmit.iris.util.hunk.Hunk; +import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; +import com.volmit.iris.util.matter.MatterBiomeInject; +import com.volmit.iris.util.nbt.mca.NBTWorld; +import com.volmit.iris.util.nbt.mca.palette.*; +import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.*; import net.minecraft.core.Registry; import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; import net.minecraft.nbt.*; import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.commands.data.BlockDataAccessor; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.tags.TagKey; import net.minecraft.world.RandomSequences; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.WorldGenContext; import net.minecraft.world.level.dimension.LevelStem; @@ -62,33 +77,17 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.volmit.iris.Iris; -import com.volmit.iris.core.nms.INMSBinding; -import com.volmit.iris.engine.data.cache.AtomicCache; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.collection.KMap; -import com.volmit.iris.util.hunk.Hunk; -import com.volmit.iris.util.json.JSONObject; -import com.volmit.iris.util.mantle.Mantle; -import com.volmit.iris.util.math.Vector3d; -import com.volmit.iris.util.matter.MatterBiomeInject; -import com.volmit.iris.util.nbt.mca.NBTWorld; -import com.volmit.iris.util.nbt.mca.palette.*; -import com.volmit.iris.util.nbt.tag.CompoundTag; - -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.LevelChunk; +import java.awt.*; +import java.awt.Color; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); @@ -673,37 +672,16 @@ public class NMSBinding implements INMSBinding { } @Override - @SuppressWarnings("all") - public KMap getMainWorlds() { - String levelName = ServerProperties.LEVEL_NAME; - KMap worlds = new KMap<>(); - for (var key : registry().lookupOrThrow(Registries.LEVEL_STEM).registryKeySet()) { - World.Environment env = World.Environment.NORMAL; - if (key == LevelStem.NETHER) { - if (!Bukkit.getAllowNether()) - continue; - env = World.Environment.NETHER; - } else if (key == LevelStem.END) { - if (!Bukkit.getAllowEnd()) - continue; - env = World.Environment.THE_END; - } else if (key != LevelStem.OVERWORLD) { - env = World.Environment.CUSTOM; - } - - String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); - worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); - } - return worlds; - } - - @Override - public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { - var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE); - if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD)); - if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); - if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); - return overworld || nether || end; + public boolean missingDimensionTypes(ChunkGenerator generator) { + if (generator == null) + return registry().lookupOrThrow(Registries.DIMENSION_TYPE) + .keySet() + .stream() + .noneMatch(loc -> loc.getNamespace().equals("iris")); + if (!(generator instanceof PlatformChunkGenerator pcg)) + return false; + var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); + return !registry().lookupOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); } @Override @@ -728,10 +706,6 @@ public class NMSBinding implements INMSBinding { return false; } - private ResourceLocation createIrisKey(ResourceKey key) { - return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); - } - public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { if (!(raw instanceof PlatformChunkGenerator gen)) throw new IllegalStateException("Generator is not platform chunk generator!"); diff --git a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java index 7f5524b3c..72f21e757 100644 --- a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java +++ b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java @@ -25,12 +25,12 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; -import net.minecraft.core.Registry; import net.minecraft.core.*; +import net.minecraft.core.Registry; import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.Tag; import net.minecraft.nbt.*; +import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; @@ -77,13 +77,14 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import java.awt.*; import java.awt.Color; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.List; import java.util.*; +import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -671,37 +672,16 @@ public class NMSBinding implements INMSBinding { } @Override - @SuppressWarnings("all") - public KMap getMainWorlds() { - String levelName = ServerProperties.LEVEL_NAME; - KMap worlds = new KMap<>(); - for (var key : registry().lookupOrThrow(Registries.LEVEL_STEM).registryKeySet()) { - World.Environment env = World.Environment.NORMAL; - if (key == LevelStem.NETHER) { - if (!Bukkit.getAllowNether()) - continue; - env = World.Environment.NETHER; - } else if (key == LevelStem.END) { - if (!Bukkit.getAllowEnd()) - continue; - env = World.Environment.THE_END; - } else if (key != LevelStem.OVERWORLD) { - env = World.Environment.CUSTOM; - } - - String worldType = env == World.Environment.CUSTOM ? key.location().getNamespace() + "_" + key.location().getPath() : env.toString().toLowerCase(Locale.ROOT); - worlds.put(key == LevelStem.OVERWORLD ? levelName : levelName + "_" + worldType, env); - } - return worlds; - } - - @Override - public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { - var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE); - if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD)); - if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); - if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); - return overworld || nether || end; + public boolean missingDimensionTypes(ChunkGenerator generator) { + if (generator == null) + return registry().lookupOrThrow(Registries.DIMENSION_TYPE) + .keySet() + .stream() + .noneMatch(loc -> loc.getNamespace().equals("iris")); + if (!(generator instanceof PlatformChunkGenerator pcg)) + return false; + var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); + return !registry().lookupOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); } @Override From 86c64f99e9364958dd9db73775757688a8b93f30 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 16 Apr 2025 23:01:57 +0200 Subject: [PATCH 018/138] filter out non iris pack directories in allPacks stream --- .../main/java/com/volmit/iris/core/ServerConfigurator.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java index b4d281c09..6db180dbc 100644 --- a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java +++ b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java @@ -228,6 +228,10 @@ public class ServerConfigurator { return Stream.concat(listFiles(new File("plugins/Iris/packs")), listFiles(Bukkit.getWorldContainer()).map(w -> new File(w, "iris/pack"))) .filter(File::isDirectory) + .filter( base -> { + var content = new File(base, "dimensions").listFiles(); + return content != null && content.length > 0; + }) .map(IrisData::get); } From 2008975a8a79eb06c3d733901f71632f2b2094d0 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Thu, 17 Apr 2025 17:30:29 +0200 Subject: [PATCH 019/138] expose most of the dimension type options --- .../volmit/iris/core/ServerConfigurator.java | 6 +- .../iris/core/nms/datapack/IDataFixer.java | 14 +- .../nms/datapack/v1192/DataFixerV1192.java | 101 +++--- .../nms/datapack/v1206/DataFixerV1206.java | 7 +- .../iris/engine/object/IrisDimension.java | 41 +-- .../iris/engine/object/IrisDimensionType.java | 91 +++++ .../object/IrisDimensionTypeOptions.java | 320 ++++++++++++++++++ 7 files changed, 499 insertions(+), 81 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/engine/object/IrisDimensionType.java create mode 100644 core/src/main/java/com/volmit/iris/engine/object/IrisDimensionTypeOptions.java diff --git a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java index 6db180dbc..f9aaee49c 100644 --- a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java +++ b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java @@ -23,9 +23,7 @@ import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.core.nms.datapack.IDataFixer; -import com.volmit.iris.engine.object.IrisBiome; -import com.volmit.iris.engine.object.IrisBiomeCustom; -import com.volmit.iris.engine.object.IrisDimension; +import com.volmit.iris.engine.object.*; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KSet; @@ -296,7 +294,7 @@ public class ServerConfigurator { int logicalHeight = data.get(2); if (minY == Integer.MAX_VALUE || maxY == Integer.MIN_VALUE || Integer.MIN_VALUE == logicalHeight) return null; - return fixer.createDimension(dimension, minY, maxY, logicalHeight).toString(4); + return fixer.createDimension(dimension, minY, maxY - minY, logicalHeight, null).toString(4); } } } diff --git a/core/src/main/java/com/volmit/iris/core/nms/datapack/IDataFixer.java b/core/src/main/java/com/volmit/iris/core/nms/datapack/IDataFixer.java index 889acddff..0e8a706a3 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/datapack/IDataFixer.java +++ b/core/src/main/java/com/volmit/iris/core/nms/datapack/IDataFixer.java @@ -1,21 +1,25 @@ package com.volmit.iris.core.nms.datapack; import com.volmit.iris.engine.object.IrisBiomeCustom; +import com.volmit.iris.engine.object.IrisDimensionTypeOptions; import com.volmit.iris.util.json.JSONObject; +import org.jetbrains.annotations.Nullable; public interface IDataFixer { - default JSONObject fixCustomBiome(IrisBiomeCustom biome, JSONObject json) { return json; } - JSONObject rawDimension(Dimension dimension); + JSONObject resolve(Dimension dimension, @Nullable IrisDimensionTypeOptions options); - default JSONObject createDimension(Dimension dimension, int minY, int maxY, int logicalHeight) { - JSONObject obj = rawDimension(dimension); + void fixDimension(Dimension dimension, JSONObject json); + + default JSONObject createDimension(Dimension base, int minY, int height, int logicalHeight, @Nullable IrisDimensionTypeOptions options) { + JSONObject obj = resolve(base, options); obj.put("min_y", minY); - obj.put("height", maxY - minY); + obj.put("height", height); obj.put("logical_height", logicalHeight); + fixDimension(base, obj); return obj; } diff --git a/core/src/main/java/com/volmit/iris/core/nms/datapack/v1192/DataFixerV1192.java b/core/src/main/java/com/volmit/iris/core/nms/datapack/v1192/DataFixerV1192.java index f2697cf38..a9bb59e0e 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/datapack/v1192/DataFixerV1192.java +++ b/core/src/main/java/com/volmit/iris/core/nms/datapack/v1192/DataFixerV1192.java @@ -1,81 +1,104 @@ package com.volmit.iris.core.nms.datapack.v1192; import com.volmit.iris.core.nms.datapack.IDataFixer; +import com.volmit.iris.engine.object.IrisDimensionTypeOptions; import com.volmit.iris.util.json.JSONObject; +import org.jetbrains.annotations.Nullable; + import java.util.Map; +import static com.volmit.iris.engine.object.IrisDimensionTypeOptions.TriState.*; + public class DataFixerV1192 implements IDataFixer { + private static final Map OPTIONS = Map.of( + Dimension.OVERWORLD, new IrisDimensionTypeOptions( + FALSE, + TRUE, + FALSE, + FALSE, + TRUE, + TRUE, + TRUE, + FALSE, + 1d, + 0f, + null, + 192, + 0), + Dimension.NETHER, new IrisDimensionTypeOptions( + TRUE, + FALSE, + TRUE, + TRUE, + FALSE, + FALSE, + FALSE, + TRUE, + 8d, + 0.1f, + 18000L, + null, + 15), + Dimension.END, new IrisDimensionTypeOptions( + FALSE, + FALSE, + FALSE, + FALSE, + FALSE, + TRUE, + FALSE, + FALSE, + 1d, + 0f, + 6000L, + null, + 0) + ); private static final Map DIMENSIONS = Map.of( Dimension.OVERWORLD, """ { - "ambient_light": 0.0, - "bed_works": true, - "coordinate_scale": 1.0, "effects": "minecraft:overworld", - "has_ceiling": false, - "has_raids": true, - "has_skylight": true, "infiniburn": "#minecraft:infiniburn_overworld", - "monster_spawn_block_light_limit": 0, "monster_spawn_light_level": { "type": "minecraft:uniform", "value": { "max_inclusive": 7, "min_inclusive": 0 } - }, - "natural": true, - "piglin_safe": false, - "respawn_anchor_works": false, - "ultrawarm": false + } }""", Dimension.NETHER, """ { - "ambient_light": 0.1, - "bed_works": false, - "coordinate_scale": 8.0, "effects": "minecraft:the_nether", - "fixed_time": 18000, - "has_ceiling": true, - "has_raids": false, - "has_skylight": false, "infiniburn": "#minecraft:infiniburn_nether", - "monster_spawn_block_light_limit": 15, "monster_spawn_light_level": 7, - "natural": false, - "piglin_safe": true, - "respawn_anchor_works": true, - "ultrawarm": true }""", Dimension.END, """ { - "ambient_light": 0.0, - "bed_works": false, - "coordinate_scale": 1.0, "effects": "minecraft:the_end", - "fixed_time": 6000, - "has_ceiling": false, - "has_raids": true, - "has_skylight": false, "infiniburn": "#minecraft:infiniburn_end", - "monster_spawn_block_light_limit": 0, "monster_spawn_light_level": { "type": "minecraft:uniform", "value": { "max_inclusive": 7, "min_inclusive": 0 } - }, - "natural": false, - "piglin_safe": false, - "respawn_anchor_works": false, - "ultrawarm": false + } }""" ); @Override - public JSONObject rawDimension(Dimension dimension) { - return new JSONObject(DIMENSIONS.get(dimension)); + public JSONObject resolve(Dimension dimension, @Nullable IrisDimensionTypeOptions options) { + return options == null ? OPTIONS.get(dimension).toJson() : options.resolve(OPTIONS.get(dimension)).toJson(); + } + + @Override + public void fixDimension(Dimension dimension, JSONObject json) { + var missing = new JSONObject(DIMENSIONS.get(dimension)); + for (String key : missing.keySet()) { + if (json.has(key)) continue; + json.put(key, missing.get(key)); + } } } diff --git a/core/src/main/java/com/volmit/iris/core/nms/datapack/v1206/DataFixerV1206.java b/core/src/main/java/com/volmit/iris/core/nms/datapack/v1206/DataFixerV1206.java index 638f043b1..eb8b59c28 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/datapack/v1206/DataFixerV1206.java +++ b/core/src/main/java/com/volmit/iris/core/nms/datapack/v1206/DataFixerV1206.java @@ -45,13 +45,12 @@ public class DataFixerV1206 extends DataFixerV1192 { } @Override - public JSONObject rawDimension(Dimension dimension) { - JSONObject json = super.rawDimension(dimension); + public void fixDimension(Dimension dimension, JSONObject json) { + super.fixDimension(dimension, json); if (!(json.get("monster_spawn_light_level") instanceof JSONObject lightLevel)) - return json; + return; var value = (JSONObject) lightLevel.remove("value"); lightLevel.put("max_inclusive", value.get("max_inclusive")); lightLevel.put("min_inclusive", value.get("min_inclusive")); - return json; } } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java index ba6b07717..7c1cb852c 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java @@ -31,7 +31,6 @@ import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.data.DataProvider; -import com.volmit.iris.util.data.Varint; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.math.Position2; @@ -47,10 +46,7 @@ import org.bukkit.Material; import org.bukkit.World.Environment; import org.bukkit.block.data.BlockData; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.IOException; +import java.io.*; @Accessors(chain = true) @AllArgsConstructor @@ -166,6 +162,8 @@ public class IrisDimension extends IrisRegistrant { private int fluidHeight = 63; @Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.") private IrisRange dimensionHeight = new IrisRange(-64, 320); + @Desc("Define options for this dimension") + private IrisDimensionTypeOptions dimensionOptions = new IrisDimensionTypeOptions(); @RegistryListResource(IrisBiome.class) @Desc("Keep this either undefined or empty. Setting any biome name into this will force iris to only generate the specified biome. Great for testing.") private String focus = ""; @@ -415,21 +413,20 @@ public class IrisDimension extends IrisRegistrant { } public String getDimensionTypeKey() { - return getDimensionTypeKey(getBaseDimension(), getMinHeight(), getMaxHeight(), getLogicalHeight()); + return getDimensionType().key(); + } + + public IrisDimensionType getDimensionType() { + return new IrisDimensionType(getBaseDimension(), getDimensionOptions(), getLogicalHeight(), getMaxHeight() - getMinHeight(), getMinHeight()); } public void installDimensionType(IDataFixer fixer, KList folders) { - String key = getDimensionTypeKey(); - String json = fixer.createDimension( - getBaseDimension(), - getMinHeight(), - getMaxHeight(), - getLogicalHeight() - ).toString(4); + IrisDimensionType type = getDimensionType(); + String json = type.toJson(fixer); - Iris.verbose(" Installing Data Pack Dimension Type: \"iris:" + key + '"'); + Iris.verbose(" Installing Data Pack Dimension Type: \"iris:" + type.key() + '"'); for (File datapacks : folders) { - File output = new File(datapacks, "iris/data/iris/dimension_type/" + key + ".json"); + File output = new File(datapacks, "iris/data/iris/dimension_type/" + type.key() + ".json"); output.getParentFile().mkdirs(); try { IO.writeAll(output, json); @@ -485,20 +482,6 @@ public class IrisDimension extends IrisRegistrant { } } - public static String getDimensionTypeKey(Dimension dimension, int minY, int maxY, int logicalHeight) { - var stream = new ByteArrayOutputStream(13); - try (var dos = new DataOutputStream(stream)) { - dos.writeByte(dimension.ordinal()); - Varint.writeUnsignedVarInt(logicalHeight, dos); - Varint.writeUnsignedVarInt(maxY - minY, dos); - Varint.writeSignedVarInt(minY, dos); - } catch (IOException e) { - throw new RuntimeException("This is impossible", e); - } - - return IO.encode(stream.toByteArray()).replace("=", ".").toLowerCase(); - } - private static void write(File datapacks, String type, String json) { if (json == null) return; File dimTypeVanilla = new File(datapacks, "iris/data/minecraft/dimension_type/" + type + ".json"); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDimensionType.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDimensionType.java new file mode 100644 index 000000000..756e4c538 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDimensionType.java @@ -0,0 +1,91 @@ +package com.volmit.iris.engine.object; + +import com.volmit.iris.core.nms.datapack.IDataFixer; +import com.volmit.iris.util.data.Varint; +import com.volmit.iris.util.io.IO; +import lombok.*; +import lombok.experimental.Accessors; + +import java.io.*; + +@Getter +@ToString +@Accessors(fluent = true, chain = true) +@EqualsAndHashCode +public final class IrisDimensionType { + @NonNull + private final String key; + @NonNull + private final IDataFixer.Dimension base; + @NonNull + private final IrisDimensionTypeOptions options; + private final int logicalHeight; + private final int height; + private final int minY; + + public IrisDimensionType( + @NonNull IDataFixer.Dimension base, + @NonNull IrisDimensionTypeOptions options, + int logicalHeight, + int height, + int minY + ) { + if (logicalHeight > height) throw new IllegalArgumentException("Logical height cannot be greater than height"); + if (logicalHeight < 0) throw new IllegalArgumentException("Logical height cannot be less than zero"); + if (height < 16 || height > 4064 ) throw new IllegalArgumentException("Height must be between 16 and 4064"); + if ((height & 15) != 0) throw new IllegalArgumentException("Height must be a multiple of 16"); + if (minY < -2032 || minY > 2031) throw new IllegalArgumentException("Min Y must be between -2032 and 2031"); + if ((minY & 15) != 0) throw new IllegalArgumentException("Min Y must be a multiple of 16"); + + this.base = base; + this.options = options; + this.logicalHeight = logicalHeight; + this.height = height; + this.minY = minY; + this.key = createKey(); + } + + public static IrisDimensionType fromKey(String key) { + var stream = new ByteArrayInputStream(IO.decode(key.replace(".", "=").toUpperCase())); + try (var din = new DataInputStream(stream)) { + return new IrisDimensionType( + IDataFixer.Dimension.values()[din.readUnsignedByte()], + new IrisDimensionTypeOptions().read(din), + Varint.readUnsignedVarInt(din), + Varint.readUnsignedVarInt(din), + Varint.readSignedVarInt(din) + ); + } catch (IOException e) { + throw new RuntimeException("This is impossible", e); + } + } + + public String toJson(IDataFixer fixer) { + return fixer.createDimension( + base, + minY, + height, + logicalHeight, + options.copy() + ).toString(4); + } + + private String createKey() { + var stream = new ByteArrayOutputStream(41); + try (var dos = new DataOutputStream(stream)) { + dos.writeByte(base.ordinal()); + options.write(dos); + Varint.writeUnsignedVarInt(logicalHeight, dos); + Varint.writeUnsignedVarInt(height, dos); + Varint.writeSignedVarInt(minY, dos); + } catch (IOException e) { + throw new RuntimeException("This is impossible", e); + } + + return IO.encode(stream.toByteArray()).replace("=", ".").toLowerCase(); + } + + public IrisDimensionTypeOptions options() { + return options.copy(); + } +} diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDimensionTypeOptions.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDimensionTypeOptions.java new file mode 100644 index 000000000..139d6296d --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDimensionTypeOptions.java @@ -0,0 +1,320 @@ +package com.volmit.iris.engine.object; + +import com.volmit.iris.engine.object.annotations.Desc; +import com.volmit.iris.engine.object.annotations.MaxNumber; +import com.volmit.iris.engine.object.annotations.MinNumber; +import com.volmit.iris.util.data.Varint; +import com.volmit.iris.util.json.JSONObject; +import lombok.*; +import lombok.experimental.Accessors; +import org.jetbrains.annotations.Nullable; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.Arrays; + +import static com.volmit.iris.engine.object.IrisDimensionTypeOptions.TriState.*; + +@Data +@NoArgsConstructor +@Accessors(fluent = true, chain = true) +@Desc("Optional options for the dimension type") +public class IrisDimensionTypeOptions { + @MinNumber(0.00001) + @MaxNumber(30000000) + @Desc("The multiplier applied to coordinates when leaving the dimension. Value between 0.00001 and 30000000.0 (both inclusive).") + private double coordinateScale = -1; + @MinNumber(0) + @MaxNumber(1) + @Desc("How much light the dimension has. When set to 0, it completely follows the light level; when set to 1, there is no ambient lighting.") + private float ambientLight = -1; + @Nullable + @MinNumber(0) + @MaxNumber(Long.MAX_VALUE) + @Desc("If this is set to an int, the time of the day is the specified value. To ensure a normal time cycle, set it to null.") + private Long fixedTime = -1L; + @Nullable + @MinNumber(-2032) + @MaxNumber(2031) + @Desc("Optional value between -2032 and 2031. If set, determines the lower edge of the clouds. If set to null, clouds are disabled in the dimension.") + private Integer cloudHeight = -1; + @MinNumber(0) + @MaxNumber(15) + @Desc("Value between 0 and 15 (both inclusive). Maximum block light required when the monster spawns.") + private int monsterSpawnBlockLightLimit = -1; + + @NonNull + @Desc("Whether the dimensions behaves like the nether (water evaporates and sponges dry) or not. Also lets stalactites drip lava and causes lava to spread faster and thinner.") + private TriState ultrawarm = DEFAULT; + @NonNull + @Desc("When false, compasses spin randomly, and using a bed to set the respawn point or sleep, is disabled. When true, nether portals can spawn zombified piglins, and creaking hearts can spawn creakings.") + private TriState natural = DEFAULT; + @NonNull + @Desc("When false, Piglins and hoglins shake and transform to zombified entities.") + private TriState piglinSafe = DEFAULT; + @NonNull + @Desc("When false, the respawn anchor blows up when trying to set spawn point.") + private TriState respawnAnchorWorks = DEFAULT; + @NonNull + @Desc("When false, the bed blows up when trying to sleep.") + private TriState bedWorks = DEFAULT; + @NonNull + @Desc("Whether players with the Bad Omen effect can cause a raid.") + private TriState raids = DEFAULT; + @NonNull + @Desc("Whether the dimension has skylight or not.") + private TriState skylight = DEFAULT; + @NonNull + @Desc("Whether the dimension has a bedrock ceiling. Note that this is only a logical ceiling. It is unrelated with whether the dimension really has a block ceiling.") + private TriState ceiling = DEFAULT; + + public IrisDimensionTypeOptions( + @NonNull TriState ultrawarm, + @NonNull TriState natural, + @NonNull TriState piglinSafe, + @NonNull TriState respawnAnchorWorks, + @NonNull TriState bedWorks, + @NonNull TriState raids, + @NonNull TriState skylight, + @NonNull TriState ceiling, + double coordinateScale, + float ambientLight, + @Nullable Long fixedTime, + @Nullable Integer cloudHeight, + int monsterSpawnBlockLightLimit + ) { + if (coordinateScale != -1 && (coordinateScale < 0.00001 || coordinateScale > 30000000)) + throw new IllegalArgumentException("Coordinate scale must be between 0.00001 and 30000000"); + if (ambientLight != -1 && (ambientLight < 0 || ambientLight > 1)) + throw new IllegalArgumentException("Ambient light must be between 0 and 1"); + if (cloudHeight != null && cloudHeight != -1 && (cloudHeight < -2032 || cloudHeight > 2031)) + throw new IllegalArgumentException("Cloud height must be between -2032 and 2031"); + if (monsterSpawnBlockLightLimit != -1 && (monsterSpawnBlockLightLimit < 0 || monsterSpawnBlockLightLimit > 15)) + throw new IllegalArgumentException("Monster spawn block light limit must be between 0 and 15"); + + this.ultrawarm = ultrawarm; + this.natural = natural; + this.piglinSafe = piglinSafe; + this.respawnAnchorWorks = respawnAnchorWorks; + this.bedWorks = bedWorks; + this.raids = raids; + this.skylight = skylight; + this.ceiling = ceiling; + this.coordinateScale = coordinateScale; + this.ambientLight = ambientLight; + this.fixedTime = fixedTime; + this.cloudHeight = cloudHeight; + this.monsterSpawnBlockLightLimit = monsterSpawnBlockLightLimit; + } + + public IrisDimensionTypeOptions coordinateScale(double coordinateScale) { + if (coordinateScale != -1 && (coordinateScale < 0.00001 || coordinateScale > 30000000)) + throw new IllegalArgumentException("Coordinate scale must be between 0.00001 and 30000000"); + this.coordinateScale = coordinateScale; + return this; + } + + public IrisDimensionTypeOptions ambientLight(float ambientLight) { + if (ambientLight != -1 && (ambientLight < 0 || ambientLight > 1)) + throw new IllegalArgumentException("Ambient light must be between 0 and 1"); + this.ambientLight = ambientLight; + return this; + } + + public IrisDimensionTypeOptions cloudHeight(@Nullable Integer cloudHeight) { + if (cloudHeight != null && cloudHeight != -1 && (cloudHeight < -2032 || cloudHeight > 2031)) + throw new IllegalArgumentException("Cloud height must be between -2032 and 2031"); + this.cloudHeight = cloudHeight; + return this; + } + + public IrisDimensionTypeOptions monsterSpawnBlockLightLimit(int monsterSpawnBlockLightLimit) { + if (monsterSpawnBlockLightLimit != -1 && (monsterSpawnBlockLightLimit < 0 || monsterSpawnBlockLightLimit > 15)) + throw new IllegalArgumentException("Monster spawn block light limit must be between 0 and 15"); + this.monsterSpawnBlockLightLimit = monsterSpawnBlockLightLimit; + return this; + } + + public void write(DataOutput dos) throws IOException { + int bits = 0; + int index = 0; + + for (TriState state : new TriState[]{ + ultrawarm, + natural, + skylight, + ceiling, + piglinSafe, + bedWorks, + respawnAnchorWorks, + raids + }) { + if (state == DEFAULT) { + index++; + continue; + } + + bits |= (short) (1 << index++); + if (state == TRUE) + bits |= (short) (1 << index++); + } + + if (coordinateScale != -1) + bits |= (1 << index++); + if (ambientLight != -1) + bits |= (1 << index++); + if (monsterSpawnBlockLightLimit != -1) + bits |= (1 << index++); + if (fixedTime != null) { + bits |= (1 << index++); + if (fixedTime != -1L) + bits |= (1 << index++); + } + if (cloudHeight != null) { + bits |= (1 << index++); + if (cloudHeight != -1) + bits |= (1 << index); + } + + Varint.writeSignedVarInt(bits, dos); + + if (coordinateScale != -1) + Varint.writeUnsignedVarLong(Double.doubleToLongBits(coordinateScale), dos); + if (ambientLight != -1) + Varint.writeUnsignedVarInt(Float.floatToIntBits(ambientLight), dos); + if (monsterSpawnBlockLightLimit != -1) + Varint.writeSignedVarInt(monsterSpawnBlockLightLimit, dos); + if (fixedTime != null && fixedTime != -1L) + Varint.writeSignedVarLong(fixedTime, dos); + if (cloudHeight != null && cloudHeight != -1) + Varint.writeSignedVarInt(cloudHeight, dos); + } + + public IrisDimensionTypeOptions read(DataInput dis) throws IOException { + TriState[] states = new TriState[8]; + Arrays.fill(states, DEFAULT); + + int bits = Varint.readSignedVarInt(dis); + int index = 0; + + for (int i = 0; i < 8; i++) { + if ((bits & (1 << index++)) == 0) + continue; + states[i] = (bits & (1 << index++)) == 0 ? FALSE : TRUE; + } + ultrawarm = states[0]; + natural = states[1]; + skylight = states[2]; + ceiling = states[3]; + piglinSafe = states[4]; + bedWorks = states[5]; + respawnAnchorWorks = states[6]; + raids = states[7]; + + coordinateScale = (bits & (1 << index++)) != 0 ? Double.longBitsToDouble(Varint.readUnsignedVarLong(dis)) : -1; + ambientLight = (bits & (1 << index++)) != 0 ? Float.intBitsToFloat(Varint.readUnsignedVarInt(dis)) : -1; + monsterSpawnBlockLightLimit = (bits & (1 << index++)) != 0 ? Varint.readSignedVarInt(dis) : -1; + fixedTime = (bits & (1 << index++)) != 0 ? (bits & (1 << index)) != 0 ? Varint.readSignedVarLong(dis) : -1L : null; + cloudHeight = (bits & (1 << index++)) != 0 ? (bits & (1 << index)) != 0 ? Varint.readSignedVarInt(dis) : -1 : null; + + return this; + } + + public IrisDimensionTypeOptions resolve(IrisDimensionTypeOptions other) { + if (ultrawarm == DEFAULT) + ultrawarm = other.ultrawarm; + if (natural == DEFAULT) + natural = other.natural; + if (piglinSafe == DEFAULT) + piglinSafe = other.piglinSafe; + if (respawnAnchorWorks == DEFAULT) + respawnAnchorWorks = other.respawnAnchorWorks; + if (bedWorks == DEFAULT) + bedWorks = other.bedWorks; + if (raids == DEFAULT) + raids = other.raids; + if (skylight == DEFAULT) + skylight = other.skylight; + if (ceiling == DEFAULT) + ceiling = other.ceiling; + if (coordinateScale == -1) + coordinateScale = other.coordinateScale; + if (ambientLight == -1) + ambientLight = other.ambientLight; + if (fixedTime != null && fixedTime == -1L) + fixedTime = other.fixedTime; + if (cloudHeight != null && cloudHeight == -1) + cloudHeight = other.cloudHeight; + if (monsterSpawnBlockLightLimit == -1) + monsterSpawnBlockLightLimit = other.monsterSpawnBlockLightLimit; + return this; + } + + public JSONObject toJson() { + if (!isComplete()) throw new IllegalStateException("Cannot serialize incomplete options"); + JSONObject json = new JSONObject(); + json.put("ultrawarm", ultrawarm.bool()); + json.put("natural", natural.bool()); + json.put("piglin_safe", piglinSafe.bool()); + json.put("respawn_anchor_works", respawnAnchorWorks.bool()); + json.put("bed_works", bedWorks.bool()); + json.put("has_raids", raids.bool()); + json.put("has_skylight", skylight.bool()); + json.put("has_ceiling", ceiling.bool()); + json.put("coordinate_scale", coordinateScale); + json.put("ambient_light", ambientLight); + json.put("monster_spawn_block_light_limit", monsterSpawnBlockLightLimit); + if (fixedTime != null) json.put("fixed_time", fixedTime); + if (cloudHeight != null) json.put("cloud_height", cloudHeight); + return json; + } + + public IrisDimensionTypeOptions copy() { + return new IrisDimensionTypeOptions( + ultrawarm, + natural, + piglinSafe, + respawnAnchorWorks, + bedWorks, + raids, + skylight, + ceiling, + coordinateScale, + ambientLight, + fixedTime, + cloudHeight, + monsterSpawnBlockLightLimit + ); + } + + public boolean isComplete() { + return ultrawarm != DEFAULT + && natural != DEFAULT + && piglinSafe != DEFAULT + && respawnAnchorWorks != DEFAULT + && bedWorks != DEFAULT + && raids != DEFAULT + && skylight != DEFAULT + && ceiling != DEFAULT + && coordinateScale != -1 + && ambientLight != -1 + && monsterSpawnBlockLightLimit != -1 + && (fixedTime == null || fixedTime != -1L) + && (cloudHeight == null || cloudHeight != -1); + } + + @Desc("Allows reusing the behavior of the base dimension") + public enum TriState { + @Desc("Follow the behavior of the base dimension") + DEFAULT, + @Desc("True") + TRUE, + @Desc("False") + FALSE; + + public boolean bool() { + return this == TRUE; + } + } +} From 0575cd85c8c5e9caf88a59a404ca8067646a3b5c Mon Sep 17 00:00:00 2001 From: dan28000 <84990628+dan28000@users.noreply.github.com> Date: Mon, 21 Apr 2025 20:44:38 +0200 Subject: [PATCH 020/138] delete old stuff delete legacy command --- .../iris/core/commands/CommandIris.java | 1 - .../iris/core/commands/CommandSupport.java | 82 ---------- .../com/volmit/iris/util/misc/Hastebin.java | 135 ---------------- .../com/volmit/iris/util/misc/Platform.java | 145 ------------------ 4 files changed, 363 deletions(-) delete mode 100644 core/src/main/java/com/volmit/iris/core/commands/CommandSupport.java delete mode 100644 core/src/main/java/com/volmit/iris/util/misc/Hastebin.java delete mode 100644 core/src/main/java/com/volmit/iris/util/misc/Platform.java diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java index 9279beb71..eed143d06 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java @@ -75,7 +75,6 @@ public class CommandIris implements DecreeExecutor { private CommandWhat what; private CommandEdit edit; private CommandFind find; - private CommandSupport support; private CommandDeveloper developer; public static boolean worldCreation = false; String WorldEngine; diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandSupport.java b/core/src/main/java/com/volmit/iris/core/commands/CommandSupport.java deleted file mode 100644 index 0f7baad0d..000000000 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandSupport.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 com.volmit.iris.core.commands; - -import com.volmit.iris.Iris; -import com.volmit.iris.core.loader.IrisData; -import com.volmit.iris.core.pregenerator.ChunkUpdater; -import com.volmit.iris.core.service.IrisEngineSVC; -import com.volmit.iris.core.tools.IrisPackBenchmarking; -import com.volmit.iris.core.tools.IrisToolbelt; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.object.IrisDimension; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.decree.DecreeExecutor; -import com.volmit.iris.util.decree.DecreeOrigin; -import com.volmit.iris.util.decree.annotations.Decree; -import com.volmit.iris.util.decree.annotations.Param; -import com.volmit.iris.util.format.C; -import com.volmit.iris.util.format.Form; -import com.volmit.iris.util.io.IO; -import com.volmit.iris.util.mantle.TectonicPlate; -import com.volmit.iris.util.misc.Hastebin; -import com.volmit.iris.util.misc.Platform; -import com.volmit.iris.util.misc.getHardware; -import com.volmit.iris.util.nbt.mca.MCAFile; -import com.volmit.iris.util.nbt.mca.MCAUtil; -import com.volmit.iris.util.plugin.VolmitSender; -import net.jpountz.lz4.LZ4BlockInputStream; -import net.jpountz.lz4.LZ4BlockOutputStream; -import net.jpountz.lz4.LZ4FrameInputStream; -import net.jpountz.lz4.LZ4FrameOutputStream; -import org.apache.commons.lang.RandomStringUtils; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.World; -import oshi.SystemInfo; - -import java.io.*; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.util.*; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -@Decree(name = "Support", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"support"}) -public class CommandSupport implements DecreeExecutor { - - @Decree(description = "report") - public void report() { - try { - if (sender().isPlayer()) sender().sendMessage(C.GOLD + "Creating report.."); - if (!sender().isPlayer()) Iris.info(C.GOLD + "Creating report.."); - Hastebin.enviornment(sender()); - - } catch (Exception e) { - Iris.info(C.RED + "Something went wrong: "); - e.printStackTrace(); - } - } - - -} - - diff --git a/core/src/main/java/com/volmit/iris/util/misc/Hastebin.java b/core/src/main/java/com/volmit/iris/util/misc/Hastebin.java deleted file mode 100644 index 11e253b31..000000000 --- a/core/src/main/java/com/volmit/iris/util/misc/Hastebin.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.volmit.iris.util.misc; - -import com.volmit.iris.Iris; -import com.volmit.iris.core.tools.IrisToolbelt; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.format.C; -import com.volmit.iris.util.format.Form; -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.TextComponent; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import oshi.SystemInfo; - -import java.io.BufferedReader; -import java.io.DataOutputStream; -import java.io.File; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.file.Files; -import java.util.List; - -public class Hastebin { - - public static void enviornment(CommandSender sender) { - // Construct the server information - StringBuilder sb = new StringBuilder(); - SystemInfo systemInfo = new SystemInfo(); - KList disks = new KList<>(getHardware.getDisk()); - KList interfaces = new KList<>(getHardware.getInterfaces()); - KList displays = new KList<>(getHardware.getEDID()); - KList sensors = new KList<>(getHardware.getSensors()); - KList gpus = new KList<>(getHardware.getGraphicsCards()); - KList powersources = new KList<>(getHardware.getPowerSources()); - - KList IrisWorlds = new KList<>(); - KList BukkitWorlds = new KList<>(); - - for (World w : Bukkit.getServer().getWorlds()) { - try { - Engine engine = IrisToolbelt.access(w).getEngine(); - if (engine != null) { - IrisWorlds.add(w); - } - } catch (Exception e) { - BukkitWorlds.add(w); - } - } - - sb.append(" -- == Iris Info == -- \n"); - sb.append("Iris Version Version: ").append(Iris.instance.getDescription().getVersion()).append("\n"); - sb.append("- Iris Worlds"); - for (World w : IrisWorlds.copy()) { - sb.append(" - ").append(w.getName()); - } - sb.append("- Bukkit Worlds"); - for (World w : BukkitWorlds.copy()) { - sb.append(" - ").append(w.getName()); - } - sb.append(" -- == Platform Overview == -- " + "\n"); - sb.append("Server Type: ").append(Bukkit.getVersion()).append("\n"); - sb.append("Server Uptime: ").append(Form.stampTime(systemInfo.getOperatingSystem().getSystemUptime())).append("\n"); - sb.append("Version: ").append(Platform.getVersion()).append(" - Platform: ").append(Platform.getName()).append("\n"); - sb.append("Java Vendor: ").append(Platform.ENVIRONMENT.getJavaVendor()).append(" - Java Version: ").append(Platform.ENVIRONMENT.getJavaVersion()).append("\n"); - sb.append(" -- == Processor Overview == -- " + "\n"); - sb.append("CPU Model: ").append(getHardware.getCPUModel()); - sb.append("CPU Architecture: ").append(Platform.CPU.getArchitecture()).append(" Available Processors: ").append(Platform.CPU.getAvailableProcessors()).append("\n"); - sb.append("CPU Load: ").append(Form.pc(Platform.CPU.getCPULoad())).append(" CPU Live Process Load: ").append(Form.pc(Platform.CPU.getLiveProcessCPULoad())).append("\n"); - sb.append("-=" + " Graphics " + "=- " + "\n"); - for (String gpu : gpus) { - sb.append(" ").append(gpu).append("\n"); - } - sb.append(" -- == Memory Information == -- " + "\n"); - sb.append("Physical Memory - Total: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getTotalMemory())).append(" Free: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getFreeMemory())).append(" Used: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getUsedMemory())).append("\n"); - sb.append("Virtual Memory - Total: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getTotalMemory())).append(" Free: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getFreeMemory())).append(" Used: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getUsedMemory())).append("\n"); - sb.append(" -- == Storage Information == -- " + "\n"); - for (String disk : disks) { - sb.append(" ").append(sb.append(disk)).append("\n"); - } - sb.append(" -- == Interface Information == -- "+ "\n" ); - for (String inter : interfaces) { - sb.append(" ").append(inter).append("\n"); - } - sb.append(" -- == Display Information == -- "+ "\n" ); - for (String display : displays) { - sb.append(display).append("\n"); - } - sb.append(" -- == Sensor Information == -- " + "\n"); - for (String sensor : sensors) { - sb.append(" ").append(sensor).append("\n"); - } - sb.append(" -- == Power Information == -- " + "\n"); - for (String power : powersources) { - sb.append(" ").append(power).append("\n"); - } - - try { - String hastebinUrl = uploadToHastebin(sb.toString()); - - // Create the clickable message - TextComponent message = new TextComponent("[Link]"); - TextComponent link = new TextComponent(hastebinUrl); - link.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, hastebinUrl)); - message.addExtra(link); - - // Send the clickable message to the player - sender.spigot().sendMessage(message); - } catch (Exception e) { - sender.sendMessage(C.DARK_RED + "Failed to upload server information to Hastebin."); - } - } - - private static String uploadToHastebin(String content) throws Exception { - URL url = new URL("https://paste.bytecode.ninja/documents"); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setRequestMethod("POST"); - conn.setRequestProperty("Content-Type", "text/plain"); - conn.setDoOutput(true); - - DataOutputStream wr = new DataOutputStream(conn.getOutputStream()); - wr.writeBytes(content); - wr.flush(); - wr.close(); - - BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); - String response = br.readLine(); - br.close(); - - return "https://paste.bytecode.ninja/" + response.split("\"")[3]; - } - - -} \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/util/misc/Platform.java b/core/src/main/java/com/volmit/iris/util/misc/Platform.java deleted file mode 100644 index afbf7c8c3..000000000 --- a/core/src/main/java/com/volmit/iris/util/misc/Platform.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.volmit.iris.util.misc; - -import com.sun.management.OperatingSystemMXBean; - -import java.io.File; -import java.lang.management.ManagementFactory; - -@SuppressWarnings("restriction") -public class Platform { - public static String getVersion() { - return getSystem().getVersion(); - } - - public static String getName() { - return getSystem().getName(); - } - - private static OperatingSystemMXBean getSystem() { - return (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); - } - - public static class ENVIRONMENT { - public static boolean canRunBatch() { - return getSystem().getName().toLowerCase().contains("windows"); - } - - public static String getJavaHome() { - return System.getProperty("java.home"); - } - - public static String getJavaVendor() { - return System.getProperty("java.vendor"); - } - - public static String getJavaVersion() { - return System.getProperty("java.version"); - } - } - - public static class STORAGE { - public static long getAbsoluteTotalSpace() { - long t = 0; - - for (File i : getRoots()) { - t += getTotalSpace(i); - } - - return t; - } - - public static long getTotalSpace() { - return getTotalSpace(new File(".")); - } - - public static long getTotalSpace(File root) { - return root.getTotalSpace(); - } - - public static long getAbsoluteFreeSpace() { - long t = 0; - - for (File i : getRoots()) { - t += getFreeSpace(i); - } - - return t; - } - - public static long getFreeSpace() { - return getFreeSpace(new File(".")); - } - - public static long getFreeSpace(File root) { - return root.getFreeSpace(); - } - - public static long getUsedSpace() { - return getTotalSpace() - getFreeSpace(); - } - - public static long getUsedSpace(File root) { - return getTotalSpace(root) - getFreeSpace(root); - } - - public static long getAbsoluteUsedSpace() { - return getAbsoluteTotalSpace() - getAbsoluteFreeSpace(); - } - - public static File[] getRoots() { - return File.listRoots(); - } - } - - public static class MEMORY { - public static class PHYSICAL { - public static long getTotalMemory() { - return getSystem().getTotalPhysicalMemorySize(); - } - - public static long getFreeMemory() { - return getSystem().getFreePhysicalMemorySize(); - } - - public static long getUsedMemory() { - return getTotalMemory() - getFreeMemory(); - } - } - - public static class VIRTUAL { - public static long getTotalMemory() { - return getSystem().getTotalSwapSpaceSize(); - } - - public static long getFreeMemory() { - return getSystem().getFreeSwapSpaceSize(); - } - - public static long getUsedMemory() { - return getTotalMemory() - getFreeMemory(); - } - - public static long getCommittedVirtualMemory() { - return getSystem().getCommittedVirtualMemorySize(); - } - } - } - - public static class CPU { - public static int getAvailableProcessors() { - return getSystem().getAvailableProcessors(); - } - - public static double getCPULoad() { - return getSystem().getSystemCpuLoad(); - } - - public static double getLiveProcessCPULoad() { - return getSystem().getProcessCpuLoad(); - } - - public static String getArchitecture() { - return getSystem().getArch(); - } - } -} \ No newline at end of file From 09cdd61a68dff77d37a0ccf507659aeee4862a31 Mon Sep 17 00:00:00 2001 From: dan28000 <84990628+dan28000@users.noreply.github.com> Date: Mon, 21 Apr 2025 20:45:20 +0200 Subject: [PATCH 021/138] fix method when having relocated worlds folder this method would fail making warning appear --- .../com/volmit/iris/core/safeguard/ServerBootSFG.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java index 2c59c2ae9..b94d7e89e 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java @@ -160,13 +160,9 @@ public class ServerBootSFG { } public static boolean enoughDiskSpace() { - File freeSpace = new File(Bukkit.getWorldContainer() + "."); + File freeSpace = Bukkit.getWorldContainer(); double gigabytes = freeSpace.getFreeSpace() / (1024.0 * 1024.0 * 1024.0); - if (gigabytes > 3){ - return true; - } else { - return false; - } + return gigabytes > 3; } private static boolean checkJavac(String path) { From e15d31a0edccdfc2d15c600f2c04241f6b599bda Mon Sep 17 00:00:00 2001 From: dan28000 <84990628+dan28000@users.noreply.github.com> Date: Mon, 21 Apr 2025 22:59:29 +0200 Subject: [PATCH 022/138] would be nice to display the right number --- .../com/volmit/iris/core/ServerConfigurator.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java index ffeae3115..b3709e43f 100644 --- a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java +++ b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java @@ -71,10 +71,12 @@ public class ServerConfigurator { f.load(spigotConfig); long tt = f.getLong("settings.timeout-time"); - if (tt < TimeUnit.MINUTES.toSeconds(5)) { - Iris.warn("Updating spigot.yml timeout-time: " + tt + " -> " + TimeUnit.MINUTES.toSeconds(20) + " (5 minutes)"); + long spigotTimeout = TimeUnit.MINUTES.toSeconds(5); + + if (tt < spigotTimeout) { + Iris.warn("Updating spigot.yml timeout-time: " + tt + " -> " + spigotTimeout + " (5 minutes)"); Iris.warn("You can disable this change (autoconfigureServer) in Iris settings, then change back the value."); - f.set("settings.timeout-time", TimeUnit.MINUTES.toSeconds(5)); + f.set("settings.timeout-time", spigotTimeout); f.save(spigotConfig); } } @@ -84,10 +86,11 @@ public class ServerConfigurator { f.load(spigotConfig); long tt = f.getLong("watchdog.early-warning-delay"); - if (tt < TimeUnit.MINUTES.toMillis(3)) { - Iris.warn("Updating paper.yml watchdog early-warning-delay: " + tt + " -> " + TimeUnit.MINUTES.toMillis(15) + " (3 minutes)"); + long watchdog = TimeUnit.MINUTES.toMillis(3); + if (tt < watchdog) { + Iris.warn("Updating paper.yml watchdog early-warning-delay: " + tt + " -> " + watchdog + " (3 minutes)"); Iris.warn("You can disable this change (autoconfigureServer) in Iris settings, then change back the value."); - f.set("watchdog.early-warning-delay", TimeUnit.MINUTES.toMillis(3)); + f.set("watchdog.early-warning-delay", watchdog); f.save(spigotConfig); } } From f93995e152cd2ce58c0d33adc10186f60b446d51 Mon Sep 17 00:00:00 2001 From: Aidan Aeternum Date: Mon, 21 Apr 2025 20:27:43 -0400 Subject: [PATCH 023/138] v+ --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ebe09d7d8..61ae21da3 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ plugins { } -version '3.6.6-1.20.1-1.21.4' +version '3.6.7-1.20.1-1.21.4' // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED // ======================== WINDOWS ============================= From 6724b0f4c5f71adba810497571920251416d4e31 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 25 Apr 2025 21:18:33 +0200 Subject: [PATCH 024/138] minor cleanup and improved safeguard for missing dimension types --- core/src/main/java/com/volmit/iris/Iris.java | 39 +++++++++++-------- .../com/volmit/iris/core/nms/INMSBinding.java | 9 +++-- .../iris/core/nms/v1X/NMSBinding1X.java | 7 +++- .../iris/core/safeguard/ServerBootSFG.java | 32 ++++++++++++++- .../iris/core/nms/v1_20_R1/NMSBinding.java | 16 +++----- .../iris/core/nms/v1_20_R2/NMSBinding.java | 16 +++----- .../iris/core/nms/v1_20_R3/NMSBinding.java | 16 +++----- .../iris/core/nms/v1_20_R4/NMSBinding.java | 16 +++----- .../iris/core/nms/v1_21_R1/NMSBinding.java | 16 +++----- .../iris/core/nms/v1_21_R2/NMSBinding.java | 16 +++----- .../iris/core/nms/v1_21_R3/NMSBinding.java | 22 +++-------- 11 files changed, 100 insertions(+), 105 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 9e2769ebb..40cc4a659 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -63,6 +63,7 @@ import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.Queue; import com.volmit.iris.util.scheduling.ShurikenQueue; import io.papermc.lib.PaperLib; +import lombok.NonNull; import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.serializer.ComponentSerializer; import org.bstats.bukkit.Metrics; @@ -748,25 +749,11 @@ public class Iris extends VolmitPlugin implements Listener { } } - IrisDimension dim; - if (id == null || id.isEmpty()) { - dim = IrisData.loadAnyDimension(IrisSettings.get().getGenerator().getDefaultWorldType()); - } else { - dim = IrisData.loadAnyDimension(id); - } + if (id == null || id.isEmpty()) id = IrisSettings.get().getGenerator().getDefaultWorldType(); Iris.debug("Generator ID: " + id + " requested by bukkit/plugin"); - + IrisDimension dim = loadDimension(worldName, id); if (dim == null) { - Iris.warn("Unable to find dimension type " + id + " Looking for online packs..."); - - service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, true); - dim = IrisData.loadAnyDimension(id); - - if (dim == null) { - throw new RuntimeException("Can't find dimension " + id + "!"); - } else { - Iris.info("Resolved missing dimension, proceeding with generation."); - } + throw new RuntimeException("Can't find dimension " + id + "!"); } Iris.debug("Assuming IrisDimension: " + dim.getName()); @@ -791,6 +778,24 @@ public class Iris extends VolmitPlugin implements Listener { return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey()); } + @Nullable + public static IrisDimension loadDimension(@NonNull String worldName, @NonNull String id) { + var data = IrisData.get(new File(Bukkit.getWorldContainer(), String.join(File.separator, worldName, "iris", "pack"))); + var dimension = data.getDimensionLoader().load(id); + if (dimension == null) dimension = IrisData.loadAnyDimension(id); + if (dimension == null) { + Iris.warn("Unable to find dimension type " + id + " Looking for online packs..."); + Iris.service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, true); + dimension = IrisData.loadAnyDimension(id); + + if (dimension != null) { + Iris.info("Resolved missing dimension, proceeding."); + } + } + + return dimension; + } + public void splash() { if (!IrisSettings.get().getGeneral().isSplashLogoStartup()) { return; diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java index 5cc90a53b..29fa4e37d 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java @@ -21,6 +21,7 @@ package com.volmit.iris.core.nms; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.mantle.Mantle; @@ -89,9 +90,9 @@ public interface INMSBinding { MCABiomeContainer newBiomeContainer(int min, int max); default World createWorld(WorldCreator c) { - if (missingDimensionTypes(c.generator())) - throw new IllegalStateException("Missing dimenstion types to create world"); - + if (c.generator() instanceof PlatformChunkGenerator gen + && missingDimensionTypes(gen.getTarget().getDimension().getDimensionTypeKey())) + throw new IllegalStateException("Missing dimension types to create world"); return c.createWorld(); } @@ -127,7 +128,7 @@ public interface INMSBinding { KList getStructureKeys(); - boolean missingDimensionTypes(@Nullable ChunkGenerator generator); + boolean missingDimensionTypes(String... keys); default boolean injectBukkit() { return true; diff --git a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java index 4b2ef8158..63a983939 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java +++ b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java @@ -120,7 +120,12 @@ public class NMSBinding1X implements INMSBinding { } @Override - public boolean missingDimensionTypes(@Nullable ChunkGenerator generator) { + public KList getLevelStems() { + return new KList<>(); + } + + @Override + public boolean missingDimensionTypes(String... keys) { return false; } diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java index 37a300a6a..f7c222db5 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java @@ -1,10 +1,15 @@ package com.volmit.iris.core.safeguard; import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.v1X.NMSBinding1X; +import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.agent.Agent; +import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.misc.ServerProperties; import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import javax.tools.JavaCompiler; @@ -184,6 +189,31 @@ public class ServerBootSFG { } private static boolean missingDimensionTypes() { - return INMS.get().missingDimensionTypes(null); + return INMS.get().missingDimensionTypes(getDimensionTypes().toArray(String[]::new)); + } + + private static KSet getDimensionTypes() { + var bukkit = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML); + var worlds = bukkit.getConfigurationSection("worlds"); + if (worlds == null) return new KSet<>(); + + var types = new KSet(); + for (String world : worlds.getKeys(false)) { + var gen = worlds.getString(world + ".generator"); + if (gen == null) continue; + + String loadKey; + if (gen.equalsIgnoreCase("iris")) { + loadKey = IrisSettings.get().getGenerator().getDefaultWorldType(); + } else if (gen.startsWith("Iris:")) { + loadKey = gen.substring(5); + } else continue; + + IrisDimension dimension = Iris.loadDimension(world, loadKey); + if (dimension == null) continue; + types.add(dimension.getDimensionTypeKey()); + } + + return types; } } diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java index 54ec3cb4d..8bb4d8182 100644 --- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java @@ -97,7 +97,6 @@ public class NMSBinding implements INMSBinding { private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -643,16 +642,11 @@ public class NMSBinding implements INMSBinding { } @Override - public boolean missingDimensionTypes(ChunkGenerator generator) { - if (generator == null) - return registry().registryOrThrow(Registries.DIMENSION_TYPE) - .keySet() - .stream() - .noneMatch(loc -> loc.getNamespace().equals("iris")); - if (!(generator instanceof PlatformChunkGenerator pcg)) - return false; - var dimensionKey = new ResourceLocation("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); - return !registry().registryOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); + public boolean missingDimensionTypes(String... keys) { + var type = registry().registryOrThrow(Registries.DIMENSION_TYPE); + return !Arrays.stream(keys) + .map(key -> new ResourceLocation("iris", key)) + .allMatch(type::containsKey); } @Override diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java index f21238b97..00224f458 100644 --- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java @@ -96,7 +96,6 @@ public class NMSBinding implements INMSBinding { private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -625,16 +624,11 @@ public class NMSBinding implements INMSBinding { } @Override - public boolean missingDimensionTypes(ChunkGenerator generator) { - if (generator == null) - return registry().registryOrThrow(Registries.DIMENSION_TYPE) - .keySet() - .stream() - .noneMatch(loc -> loc.getNamespace().equals("iris")); - if (!(generator instanceof PlatformChunkGenerator pcg)) - return false; - var dimensionKey = new ResourceLocation("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); - return !registry().registryOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); + public boolean missingDimensionTypes(String... keys) { + var type = registry().registryOrThrow(Registries.DIMENSION_TYPE); + return !Arrays.stream(keys) + .map(key -> new ResourceLocation("iris", key)) + .allMatch(type::containsKey); } private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java index f9b960626..fb47198fc 100644 --- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java @@ -96,7 +96,6 @@ public class NMSBinding implements INMSBinding { private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -647,16 +646,11 @@ public class NMSBinding implements INMSBinding { } @Override - public boolean missingDimensionTypes(ChunkGenerator generator) { - if (generator == null) - return registry().registryOrThrow(Registries.DIMENSION_TYPE) - .keySet() - .stream() - .noneMatch(loc -> loc.getNamespace().equals("iris")); - if (!(generator instanceof PlatformChunkGenerator pcg)) - return false; - var dimensionKey = new ResourceLocation("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); - return !registry().registryOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); + public boolean missingDimensionTypes(String... keys) { + var type = registry().registryOrThrow(Registries.DIMENSION_TYPE); + return !Arrays.stream(keys) + .map(key -> new ResourceLocation("iris", key)) + .allMatch(type::containsKey); } @Override diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java index 7fe71001d..14e02eabc 100644 --- a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java @@ -97,7 +97,6 @@ public class NMSBinding implements INMSBinding { private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -665,16 +664,11 @@ public class NMSBinding implements INMSBinding { } @Override - public boolean missingDimensionTypes(ChunkGenerator generator) { - if (generator == null) - return registry().registryOrThrow(Registries.DIMENSION_TYPE) - .keySet() - .stream() - .noneMatch(loc -> loc.getNamespace().equals("iris")); - if (!(generator instanceof PlatformChunkGenerator pcg)) - return false; - var dimensionKey = new ResourceLocation("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); - return !registry().registryOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); + public boolean missingDimensionTypes(String... keys) { + var type = registry().registryOrThrow(Registries.DIMENSION_TYPE); + return !Arrays.stream(keys) + .map(key -> new ResourceLocation("iris", key)) + .allMatch(type::containsKey); } @Override diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java index 18b7daac9..035f2cbb6 100644 --- a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java +++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java @@ -100,7 +100,6 @@ public class NMSBinding implements INMSBinding { private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -675,16 +674,11 @@ public class NMSBinding implements INMSBinding { } @Override - public boolean missingDimensionTypes(ChunkGenerator generator) { - if (generator == null) - return registry().registryOrThrow(Registries.DIMENSION_TYPE) - .keySet() - .stream() - .noneMatch(loc -> loc.getNamespace().equals("iris")); - if (!(generator instanceof PlatformChunkGenerator pcg)) - return false; - var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); - return !registry().registryOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); + public boolean missingDimensionTypes(String... keys) { + var type = registry().registryOrThrow(Registries.DIMENSION_TYPE); + return !Arrays.stream(keys) + .map(key -> ResourceLocation.fromNamespaceAndPath("iris", key)) + .allMatch(type::containsKey); } @Override diff --git a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java index 7b0fb012b..93019eb7b 100644 --- a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java +++ b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java @@ -97,7 +97,6 @@ public class NMSBinding implements INMSBinding { private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -672,16 +671,11 @@ public class NMSBinding implements INMSBinding { } @Override - public boolean missingDimensionTypes(ChunkGenerator generator) { - if (generator == null) - return registry().lookupOrThrow(Registries.DIMENSION_TYPE) - .keySet() - .stream() - .noneMatch(loc -> loc.getNamespace().equals("iris")); - if (!(generator instanceof PlatformChunkGenerator pcg)) - return false; - var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); - return !registry().lookupOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); + public boolean missingDimensionTypes(String... keys) { + var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE); + return !Arrays.stream(keys) + .map(key -> ResourceLocation.fromNamespaceAndPath("iris", key)) + .allMatch(type::containsKey); } @Override diff --git a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java index 72f21e757..cf02a9ddb 100644 --- a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java +++ b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java @@ -11,6 +11,7 @@ import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.agent.Agent; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.format.C; import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.json.JSONObject; @@ -77,7 +78,6 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -import java.awt.*; import java.awt.Color; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -97,7 +97,6 @@ public class NMSBinding implements INMSBinding { private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final KMap stems = new KMap<>(); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -672,16 +671,11 @@ public class NMSBinding implements INMSBinding { } @Override - public boolean missingDimensionTypes(ChunkGenerator generator) { - if (generator == null) - return registry().lookupOrThrow(Registries.DIMENSION_TYPE) - .keySet() - .stream() - .noneMatch(loc -> loc.getNamespace().equals("iris")); - if (!(generator instanceof PlatformChunkGenerator pcg)) - return false; - var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", pcg.getTarget().getDimension().getDimensionTypeKey()); - return !registry().lookupOrThrow(Registries.DIMENSION_TYPE).containsKey(dimensionKey); + public boolean missingDimensionTypes(String... keys) { + var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE); + return !Arrays.stream(keys) + .map(key -> ResourceLocation.fromNamespaceAndPath("iris", key)) + .allMatch(type::containsKey); } @Override @@ -706,10 +700,6 @@ public class NMSBinding implements INMSBinding { return false; } - private ResourceLocation createIrisKey(ResourceKey key) { - return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); - } - public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { if (!(raw instanceof PlatformChunkGenerator gen)) throw new IllegalStateException("Generator is not platform chunk generator!"); From 976648340e963ef47e67f89edc0d2bff06e8bd11 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 16 May 2025 12:02:38 +0200 Subject: [PATCH 025/138] add pregen method that doesn't use waiting threads --- .../com/volmit/iris/core/IrisSettings.java | 3 + .../methods/AsyncPregenMethod.java | 104 ++++++++++++------ .../volmit/iris/core/tools/IrisToolbelt.java | 2 +- 3 files changed, 76 insertions(+), 33 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index c26b09b3b..826746193 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -145,7 +145,10 @@ public class IrisSettings { @Data public static class IrisSettingsPregen { + public boolean useCacheByDefault = true; + public boolean useHighPriority = false; public boolean useVirtualThreads = false; + public boolean useTicketQueue = false; public int maxConcurrency = 256; } diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java index c31bf5029..757dfcf8f 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java @@ -33,7 +33,6 @@ import org.bukkit.Chunk; import org.bukkit.World; import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -43,22 +42,22 @@ import java.util.concurrent.atomic.AtomicInteger; public class AsyncPregenMethod implements PregeneratorMethod { private static final AtomicInteger THREAD_COUNT = new AtomicInteger(); private final World world; - private final ExecutorService service; + private final Executor executor; private final Semaphore semaphore; private final int threads; + private final boolean urgent; private final Map lastUse; - public AsyncPregenMethod(World world, int threads) { + public AsyncPregenMethod(World world, int unusedThreads) { if (!PaperLib.isPaper()) { throw new UnsupportedOperationException("Cannot use PaperAsync on non paper!"); } this.world = world; - service = IrisSettings.get().getPregen().isUseVirtualThreads() ? - Executors.newVirtualThreadPerTaskExecutor() : - new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY); + this.executor = IrisSettings.get().getPregen().isUseTicketQueue() ? new TicketExecutor() : new ServiceExecutor(); this.threads = IrisSettings.get().getPregen().getMaxConcurrency(); - semaphore = new Semaphore(threads); + this.semaphore = new Semaphore(this.threads, true); + this.urgent = IrisSettings.get().getPregen().useHighPriority; this.lastUse = new KMap<>(); } @@ -70,13 +69,18 @@ public class AsyncPregenMethod implements PregeneratorMethod { return; } - for (Chunk i : new ArrayList<>(lastUse.keySet())) { - Long lastUseTime = lastUse.get(i); - if (!i.isLoaded() || (lastUseTime != null && M.ms() - lastUseTime >= 10000)) { - i.unload(); - lastUse.remove(i); + long minTime = M.ms() - 10_000; + lastUse.entrySet().removeIf(i -> { + final Chunk chunk = i.getKey(); + final Long lastUseTime = i.getValue(); + if (!chunk.isLoaded() || lastUseTime == null) + return true; + if (lastUseTime < minTime) { + chunk.unload(); + return true; } - } + return false; + }); world.save(); }).get(); } catch (Throwable e) { @@ -84,21 +88,6 @@ public class AsyncPregenMethod implements PregeneratorMethod { } } - private void completeChunk(int x, int z, PregenListener listener) { - try { - PaperLib.getChunkAtAsync(world, x, z, true).thenAccept((i) -> { - lastUse.put(i, M.ms()); - listener.onChunkGenerated(x, z); - listener.onChunkCleaned(x, z); - }).get(); - } catch (InterruptedException ignored) { - } catch (Throwable e) { - e.printStackTrace(); - } finally { - semaphore.release(); - } - } - @Override public void init() { unloadAndSaveAllChunks(); @@ -114,7 +103,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { public void close() { semaphore.acquireUninterruptibly(threads); unloadAndSaveAllChunks(); - service.shutdown(); + executor.shutdown(); resetWorkerThreads(); } @@ -141,7 +130,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { } catch (InterruptedException e) { return; } - service.submit(() -> completeChunk(x, z, listener)); + executor.generate(x, z, listener); } @Override @@ -153,7 +142,6 @@ public class AsyncPregenMethod implements PregeneratorMethod { return null; } - public static void increaseWorkerThreads() { THREAD_COUNT.updateAndGet(i -> { if (i > 0) return 1; @@ -167,7 +155,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { pool.getClass().getDeclaredMethod("adjustThreadCount", int.class).invoke(pool, adjusted); return threads; } catch (Throwable e) { - Iris.warn("Failed to increase worker threads, please increase it manually to " + adjusted); + Iris.warn("Failed to increase worker threads, if you are on paper or a fork of it please increase it manually to " + adjusted); Iris.warn("For more information see https://docs.papermc.io/paper/reference/global-configuration#chunk_system_worker_threads"); if (e instanceof InvocationTargetException) e.printStackTrace(); } @@ -191,4 +179,56 @@ public class AsyncPregenMethod implements PregeneratorMethod { return i; }); } + + private interface Executor { + void generate(int x, int z, PregenListener listener); + default void shutdown() {} + } + + private class ServiceExecutor implements Executor { + private final ExecutorService service = IrisSettings.get().getPregen().isUseVirtualThreads() ? + Executors.newVirtualThreadPerTaskExecutor() : + new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY); + + public void generate(int x, int z, PregenListener listener) { + service.submit(() -> { + try { + PaperLib.getChunkAtAsync(world, x, z, true, urgent).thenAccept((i) -> { + listener.onChunkGenerated(x, z); + listener.onChunkCleaned(x, z); + if (i == null) return; + lastUse.put(i, M.ms()); + }).get(); + } catch (InterruptedException ignored) { + } catch (Throwable e) { + e.printStackTrace(); + } finally { + semaphore.release(); + } + }); + } + + @Override + public void shutdown() { + service.shutdown(); + } + } + + private class TicketExecutor implements Executor { + @Override + public void generate(int x, int z, PregenListener listener) { + PaperLib.getChunkAtAsync(world, x, z, true, urgent) + .exceptionally(e -> { + e.printStackTrace(); + return null; + }) + .thenAccept(i -> { + semaphore.release(); + listener.onChunkGenerated(x, z); + listener.onChunkCleaned(x, z); + if (i == null) return; + lastUse.put(i, M.ms()); + }); + } + } } diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java b/core/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java index 15b3dc8d3..06651213c 100644 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java @@ -142,7 +142,7 @@ public class IrisToolbelt { * @return the pregenerator job (already started) */ public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine engine) { - return pregenerate(task, method, engine, true); + return pregenerate(task, method, engine, IrisSettings.get().getPregen().useCacheByDefault); } /** From 9152b25d51c7973d4a299e448cea0f5ce6d53e8b Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 16 Apr 2025 16:21:30 +0200 Subject: [PATCH 026/138] fix pack hash calculation --- .../com/volmit/iris/engine/IrisEngine.java | 5 ++- .../main/java/com/volmit/iris/util/io/IO.java | 42 +++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/IrisEngine.java b/core/src/main/java/com/volmit/iris/engine/IrisEngine.java index 88c5fdc14..7224ea295 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisEngine.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisEngine.java @@ -180,7 +180,10 @@ public class IrisEngine implements Engine { File[] roots = getData().getLoaders() .values() .stream() - .map(ResourceLoader::getRoot) + .map(ResourceLoader::getFolderName) + .map(n -> new File(getData().getDataFolder(), n)) + .filter(File::exists) + .filter(File::isDirectory) .toArray(File[]::new); hash32.complete(IO.hashRecursive(roots)); }); diff --git a/core/src/main/java/com/volmit/iris/util/io/IO.java b/core/src/main/java/com/volmit/iris/util/io/IO.java index a8f718885..554058244 100644 --- a/core/src/main/java/com/volmit/iris/util/io/IO.java +++ b/core/src/main/java/com/volmit/iris/util/io/IO.java @@ -18,6 +18,10 @@ package com.volmit.iris.util.io; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.volmit.iris.Iris; import com.volmit.iris.util.format.Form; @@ -134,8 +138,7 @@ public class IO { continue; } - try (var fin = new FileInputStream(file)) { - var din = new CheckedInputStream(fin, crc); + try (var din = new CheckedInputStream(readDeterministic(file), crc)) { fullTransfer(din, new VoidOutputStream(), 8192); } catch (IOException e) { Iris.reportError(e); @@ -152,10 +155,43 @@ public class IO { return 0; } + public static InputStream readDeterministic(File file) throws IOException { + if (!file.getName().endsWith(".json")) + return new FileInputStream(file); + + JsonElement json; + try (FileReader reader = new FileReader(file)) { + json = JsonParser.parseReader(reader); + } + + var queue = new LinkedList(); + queue.add(json); + + while (!queue.isEmpty()) { + var element = queue.pop(); + Collection add = List.of(); + + if (element instanceof JsonObject obj) { + var map = obj.asMap(); + var sorted = new TreeMap<>(map); + map.clear(); + map.putAll(sorted); + + add = sorted.values(); + } else if (element instanceof JsonArray array) { + add = array.asList(); + } + + add.stream().filter(e -> e.isJsonObject() || e.isJsonArray()).forEach(queue::add); + } + + return toInputStream(json.toString()); + } + public static String hash(File b) { try { MessageDigest d = MessageDigest.getInstance("SHA-256"); - DigestInputStream din = new DigestInputStream(new FileInputStream(b), d); + DigestInputStream din = new DigestInputStream(readDeterministic(b), d); fullTransfer(din, new VoidOutputStream(), 8192); din.close(); return bytesToHex(din.getMessageDigest().digest()); From ce29dc98c36e7828695aa4473ad7f81b7e5cab17 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Thu, 10 Apr 2025 21:01:06 +0200 Subject: [PATCH 027/138] fix spawn markers not being removed --- core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java | 2 +- .../java/com/volmit/iris/engine/object/IrisEntitySpawn.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java b/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java index 08962e4af..28e48bd18 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java @@ -367,7 +367,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager { private void spawn(IrisPosition pos, IrisEntitySpawn i) { IrisSpawner ref = i.getReferenceSpawner(); - if (!ref.canSpawn(getEngine(), pos.getX() >> 4, pos.getZ())) + if (!ref.canSpawn(getEngine(), pos.getX() >> 4, pos.getZ() >> 4)) return; int s = i.spawn(getEngine(), pos, RNG.r); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisEntitySpawn.java b/core/src/main/java/com/volmit/iris/engine/object/IrisEntitySpawn.java index a507c342f..c93ffaade 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisEntitySpawn.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisEntitySpawn.java @@ -117,7 +117,7 @@ public class IrisEntitySpawn implements IRare { if (spawns > 0) { if (referenceMarker != null) { - gen.getMantle().getMantle().remove(c.getX(), c.getY(), c.getZ(), MatterMarker.class); + gen.getMantle().getMantle().remove(c.getX(), c.getY() - gen.getWorld().minHeight(), c.getZ(), MatterMarker.class); } for (int id = 0; id < spawns; id++) { From 5d42c5cae0e6695c64e869b95726c41fcefe76ee Mon Sep 17 00:00:00 2001 From: Aidan Aeternum Date: Tue, 20 May 2025 12:32:04 -0400 Subject: [PATCH 028/138] v+ --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 61ae21da3..b385555b4 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ plugins { } -version '3.6.7-1.20.1-1.21.4' +version '3.6.8-1.20.1-1.21.4' // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED // ======================== WINDOWS ============================= From 97ddfd309b23cb13bb70541c5527c082edec06e8 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 21 May 2025 13:05:07 +0200 Subject: [PATCH 029/138] fix updater not working --- .../volmit/iris/core/pregenerator/ChunkUpdater.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java b/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java index cb8407272..9f9bde99e 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java @@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; public class ChunkUpdater { + private static final String REGION_PATH = "region" + File.separator + "r."; private final AtomicBoolean paused = new AtomicBoolean(); private final AtomicBoolean cancelled = new AtomicBoolean(); private final KMap> lastUse = new KMap<>(); @@ -162,12 +163,12 @@ public class ChunkUpdater { J.sleep(50); } - if (rX < dimensions.min.getX() || rX > dimensions.max.getX() || rZ < dimensions.min.getZ() || rZ > dimensions.max.getZ()) { - return; - } - if (!new File(world.getWorldFolder(), "region" + File.separator + rX + "." + rZ + ".mca").exists()) { - return; - } + if (rX < dimensions.min.getX() || + rX > dimensions.max.getX() || + rZ < dimensions.min.getZ() || + rZ > dimensions.max.getZ() || + !new File(world.getWorldFolder(), REGION_PATH + rX + "." + rZ + ".mca").exists() + ) return; task.iterateChunks(rX, rZ, (x, z) -> { while (paused.get() && !cancelled.get()) { From f68600464bed47f07aaa97aa587dd4b757682ea9 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 25 May 2025 18:12:48 +0200 Subject: [PATCH 030/138] remove redundant method from NMSBinding1X --- .../java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java index 63a983939..642a00e38 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java +++ b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java @@ -37,7 +37,6 @@ import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.structure.Structure; import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; import java.awt.Color; import java.util.stream.StreamSupport; @@ -119,11 +118,6 @@ public class NMSBinding1X implements INMSBinding { return new KList<>(list); } - @Override - public KList getLevelStems() { - return new KList<>(); - } - @Override public boolean missingDimensionTypes(String... keys) { return false; From e42317139d568499af3a80f8314b5e3f460db9af Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 21 May 2025 13:09:08 +0200 Subject: [PATCH 031/138] fix engines not closing on server stop --- core/src/main/java/com/volmit/iris/Iris.java | 7 +- .../volmit/iris/engine/framework/Engine.java | 131 +++++++++--------- .../com/volmit/iris/util/mantle/Mantle.java | 23 +-- .../volmit/iris/util/mantle/MantleChunk.java | 19 ++- .../iris/util/mantle/TectonicPlate.java | 9 ++ .../volmit/iris/util/parallel/HyperLock.java | 1 + .../volmit/iris/util/parallel/MultiBurst.java | 3 +- 7 files changed, 111 insertions(+), 82 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 64fa8d4d3..51270bb4c 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -572,12 +572,17 @@ public class Iris extends VolmitPlugin implements Listener { } public void onDisable() { + Bukkit.getWorlds() + .parallelStream() + .map(IrisToolbelt::access) + .filter(Objects::nonNull) + .forEach(PlatformChunkGenerator::close); services.values().forEach(IrisService::onDisable); Bukkit.getScheduler().cancelTasks(this); HandlerList.unregisterAll((Plugin) this); postShutdown.forEach(Runnable::run); - services.clear(); MultiBurst.burst.close(); + services.clear(); super.onDisable(); } diff --git a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java index fe99a322d..9a87d26e9 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java @@ -288,76 +288,79 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat return; } - var chunk = mantle.getChunk(c); - if (chunk.isFlagged(MantleFlag.ETCHED)) return; - chunk.flag(MantleFlag.ETCHED, true); + var chunk = mantle.getChunk(c).use(); + try { + Semaphore semaphore = new Semaphore(3); + chunk.raiseFlag(MantleFlag.ETCHED, () -> { + chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> { + mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> { + int betterY = y + getWorld().minHeight(); + if (!TileData.setTileState(c.getBlock(x, betterY, z), v.getData())) + Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), v.getData().getMaterial().name()); + }); + }))); + chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> { + mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> { + Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v); + }); + }))); - Semaphore semaphore = new Semaphore(3); - chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> { - mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> { - int betterY = y + getWorld().minHeight(); - if (!TileData.setTileState(c.getBlock(x, betterY, z), v.getData())) - Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), v.getData().getMaterial().name()); - }); - }))); - chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> { - mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> { - Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v); - }); - }))); - - chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> J.s(() -> { - PrecisionStopwatch p = PrecisionStopwatch.start(); - KMap updates = new KMap<>(); - RNG r = new RNG(Cache.key(c.getX(), c.getZ())); - mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> { - int y = yf + getWorld().minHeight(); - if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) { - return; - } - boolean u = false; - if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) { - u = true; - } else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) { - u = true; - } else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) { - u = true; - } else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) { - u = true; - } else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) { - u = true; - } - - if (u) { - updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> { - if (vv != null) { - return Math.max(vv, y); + chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> J.s(() -> { + PrecisionStopwatch p = PrecisionStopwatch.start(); + KMap updates = new KMap<>(); + RNG r = new RNG(Cache.key(c.getX(), c.getZ())); + mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> { + int y = yf + getWorld().minHeight(); + if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) { + return; + } + boolean u = false; + if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) { + u = true; + } else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) { + u = true; + } else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) { + u = true; + } else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) { + u = true; + } else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) { + u = true; } - return y; + if (u) { + updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> { + if (vv != null) { + return Math.max(vv, y); + } + + return y; + }); + } }); - } + + updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r)); + mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> { + int y = yf + getWorld().minHeight(); + if (v != null && v.isUpdate()) { + int vx = x & 15; + int vz = z & 15; + update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ()))); + if (vx > 0 && vx < 15 && vz > 0 && vz < 15) { + updateLighting(x, y, z, c); + } + } + }); + mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class); + getMetrics().getUpdates().put(p.getMilliseconds()); + }, RNG.r.i(0, 20)))); }); - updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r)); - mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> { - int y = yf + getWorld().minHeight(); - if (v != null && v.isUpdate()) { - int vx = x & 15; - int vz = z & 15; - update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ()))); - if (vx > 0 && vx < 15 && vz > 0 && vz < 15) { - updateLighting(x, y, z, c); - } - } - }); - mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class); - getMetrics().getUpdates().put(p.getMilliseconds()); - }, RNG.r.i(0, 20)))); - - try { - semaphore.acquire(3); - } catch (InterruptedException ignored) {} + try { + semaphore.acquire(3); + } catch (InterruptedException ignored) {} + } finally { + chunk.release(); + } } private static Runnable run(Semaphore semaphore, Runnable runnable) { diff --git a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java index 0e89c1162..3ae6ad880 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -359,16 +359,18 @@ public class Mantle { } closed.set(true); - BurstExecutor b = ioBurst.burst(loadedRegions.size()); - for (Long i : loadedRegions.keySet()) { - b.queue(() -> { - try { - loadedRegions.get(i).write(fileForRegion(dataFolder, i)); - } catch (IOException e) { - e.printStackTrace(); - } - }); - } + hyperLock.disable(); + BurstExecutor b = ioBurst.burst(toUnload.size()); + loadedRegions.forEach((i, plate) -> b.queue(() -> { + try { + plate.close(); + plate.write(fileForRegion(dataFolder, i)); + } catch (Throwable e) { + Iris.error("Failed to write Tectonic Plate " + C.DARK_GREEN + Cache.keyX(i) + " " + Cache.keyZ(i)); + e.printStackTrace(); + } + })); + loadedRegions.clear(); try { b.complete(); @@ -376,7 +378,6 @@ public class Mantle { Iris.reportError(e); } - loadedRegions.clear(); Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath()); } diff --git a/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java b/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java index 2d3c2a114..0cfea94d8 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java @@ -30,7 +30,8 @@ import lombok.Getter; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicIntegerArray; import java.util.concurrent.atomic.AtomicReferenceArray; @@ -45,7 +46,8 @@ public class MantleChunk { private final int z; private final AtomicIntegerArray flags; private final AtomicReferenceArray sections; - private final AtomicInteger ref = new AtomicInteger(); + private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true); + private final AtomicBoolean closed = new AtomicBoolean(false); /** * Create a mantle chunk @@ -103,20 +105,27 @@ public class MantleChunk { } } + public void close() throws InterruptedException { + closed.set(true); + ref.acquire(Integer.MAX_VALUE); + } + public boolean inUse() { - return ref.get() > 0; + return ref.availablePermits() < Integer.MAX_VALUE; } public MantleChunk use() { - ref.incrementAndGet(); + if (closed.get()) throw new IllegalStateException("Chunk is closed!"); + ref.acquireUninterruptibly(); return this; } public void release() { - ref.decrementAndGet(); + ref.release(); } public void flag(MantleFlag flag, boolean f) { + if (closed.get()) throw new IllegalStateException("Chunk is closed!"); flags.set(flag.ordinal(), f ? 1 : 0); } diff --git a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java index efd724d5e..7c59d856a 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java @@ -136,6 +136,15 @@ public class TectonicPlate { return false; } + public void close() throws InterruptedException { + for (int i = 0; i < chunks.length(); i++) { + MantleChunk chunk = chunks.get(i); + if (chunk != null) { + chunk.close(); + } + } + } + /** * Check if a chunk exists in this plate or not (same as get(x, z) != null) * diff --git a/core/src/main/java/com/volmit/iris/util/parallel/HyperLock.java b/core/src/main/java/com/volmit/iris/util/parallel/HyperLock.java index c40294552..6d0eaaee0 100644 --- a/core/src/main/java/com/volmit/iris/util/parallel/HyperLock.java +++ b/core/src/main/java/com/volmit/iris/util/parallel/HyperLock.java @@ -143,5 +143,6 @@ public class HyperLock { public void disable() { enabled = false; + locks.values().forEach(ReentrantLock::lock); } } diff --git a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java index 8a71ac48c..500a4a0b2 100644 --- a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java +++ b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java @@ -32,6 +32,7 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; public class MultiBurst implements ExecutorService { + private static final long TIMEOUT = Long.getLong("iris.burst.timeout", 15000); public static final MultiBurst burst = new MultiBurst(); private final AtomicLong last; private final String name; @@ -231,7 +232,7 @@ public class MultiBurst implements ExecutorService { try { while (!service.awaitTermination(1, TimeUnit.SECONDS)) { Iris.info("Still waiting to shutdown burster..."); - if (p.getMilliseconds() > 7000) { + if (p.getMilliseconds() > TIMEOUT) { Iris.warn("Forcing Shutdown..."); try { From 61301ffd4d083d5469c49888be4467aebfec1369 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 25 May 2025 13:46:02 +0200 Subject: [PATCH 032/138] close engines using a shutdown hook --- core/src/main/java/com/volmit/iris/Iris.java | 19 ++++++++++++------- .../engine/platform/BukkitChunkGenerator.java | 4 +++- .../com/volmit/iris/util/io/JarScanner.java | 11 +++++++++-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 51270bb4c..5bf32b68f 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -572,18 +572,23 @@ public class Iris extends VolmitPlugin implements Listener { } public void onDisable() { - Bukkit.getWorlds() - .parallelStream() - .map(IrisToolbelt::access) - .filter(Objects::nonNull) - .forEach(PlatformChunkGenerator::close); services.values().forEach(IrisService::onDisable); Bukkit.getScheduler().cancelTasks(this); HandlerList.unregisterAll((Plugin) this); postShutdown.forEach(Runnable::run); - MultiBurst.burst.close(); - services.clear(); super.onDisable(); + + J.attempt(new JarScanner(instance.getJarFile(), "", false)::scan); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + Bukkit.getWorlds() + .stream() + .map(IrisToolbelt::access) + .filter(Objects::nonNull) + .forEach(PlatformChunkGenerator::close); + + MultiBurst.burst.close(); + services.clear(); + })); } private void setupPapi() { diff --git a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index 90117a5a4..c2ed23c83 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -301,7 +301,9 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun hotloader.interrupt(); } - getEngine().close(); + final Engine engine = getEngine(); + if (engine != null && !engine.isClosed()) + engine.close(); folder.clear(); populators.clear(); diff --git a/core/src/main/java/com/volmit/iris/util/io/JarScanner.java b/core/src/main/java/com/volmit/iris/util/io/JarScanner.java index 2d8c86e1b..d7dcd31e7 100644 --- a/core/src/main/java/com/volmit/iris/util/io/JarScanner.java +++ b/core/src/main/java/com/volmit/iris/util/io/JarScanner.java @@ -31,16 +31,22 @@ public class JarScanner { private final KSet> classes; private final File jar; private final String superPackage; + private final boolean report; /** * Create a scanner * * @param jar the path to the jar */ - public JarScanner(File jar, String superPackage) { + public JarScanner(File jar, String superPackage, boolean report) { this.jar = jar; this.classes = new KSet<>(); this.superPackage = superPackage; + this.report = report; + } + + public JarScanner(File jar, String superPackage) { + this(jar, superPackage, true); } /** @@ -65,7 +71,8 @@ public class JarScanner { try { Class clazz = Class.forName(c); classes.add(clazz); - } catch (ClassNotFoundException e) { + } catch (Throwable e) { + if (!report) continue; Iris.reportError(e); e.printStackTrace(); } From 90c6457d37841c26ee48c9d6958f8b7b08f3c394 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 25 May 2025 14:00:07 +0200 Subject: [PATCH 033/138] write to plates to a temp file first and then move it into the mantle dir --- .../com/volmit/iris/util/mantle/TectonicPlate.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java index 7c59d856a..e635d4649 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java @@ -217,15 +217,13 @@ public class TectonicPlate { */ public void write(File file) throws IOException { PrecisionStopwatch p = PrecisionStopwatch.start(); - try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.SYNC)) { - fc.lock(); - - OutputStream fos = Channels.newOutputStream(fc); - try (DataOutputStream dos = new DataOutputStream(new LZ4BlockOutputStream(fos))) { - write(dos); - Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName().split("\\Q.\\E")[0] + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); - } + File temp = File.createTempFile("iris-tectonic-plate", ".bin"); + try (DataOutputStream dos = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(temp)))) { + write(dos); } + Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); + Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName().split("\\Q.\\E")[0] + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); + temp.delete(); } /** From f0476fea9b048e42f8a7a4d10a891c7321cfa8f8 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 26 May 2025 20:54:12 +0200 Subject: [PATCH 034/138] implement version header for tectonic plates --- build.gradle | 5 ++ .../com/volmit/iris/core/IrisSettings.java | 1 + .../iris/core/commands/CommandDeveloper.java | 9 ++-- .../iris/util/atomics/AtomicAverage.java | 12 ++--- .../com/volmit/iris/util/collection/KSet.java | 54 ++++++++++++++++--- .../com/volmit/iris/util/mantle/Mantle.java | 38 +++++++++---- .../volmit/iris/util/mantle/MantleChunk.java | 11 +++- .../iris/util/mantle/TectonicPlate.java | 24 +++++---- .../com/volmit/iris/util/matter/Matter.java | 17 +++--- 9 files changed, 124 insertions(+), 47 deletions(-) diff --git a/build.gradle b/build.gradle index b385555b4..67610d095 100644 --- a/build.gradle +++ b/build.gradle @@ -113,6 +113,11 @@ shadowJar { relocate 'net.kyori', 'com.volmit.iris.util.kyori' relocate 'org.bstats', 'com.volmit.util.metrics' archiveFileName.set("Iris-${project.version}.jar") + + dependencies { + exclude(dependency("org.ow2.asm:asm:")) + exclude(dependency("org.jetbrains:")) + } } dependencies { diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 826746193..1a86aad11 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -191,6 +191,7 @@ public class IrisSettings { public boolean DoomsdayAnnihilationSelfDestructMode = false; public boolean commandSounds = true; public boolean debug = false; + public boolean dumpMantleOnError = false; public boolean disableNMS = false; public boolean pluginMetrics = true; public boolean splashLogoStartup = true; diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java index f9590151a..d54a97e5b 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java @@ -166,7 +166,7 @@ public class CommandDeveloper implements DecreeExecutor { File tectonicplates = new File(folder, "mantle"); for (File i : Objects.requireNonNull(tectonicplates.listFiles())) { - TectonicPlate.read(maxHeight, i); + TectonicPlate.read(maxHeight, i, true); c++; Iris.info("Loaded count: " + c ); @@ -272,7 +272,8 @@ public class CommandDeveloper implements DecreeExecutor { @Param(description = "base IrisWorld") World world, @Param(description = "raw TectonicPlate File") String path, @Param(description = "Algorithm to Test") String algorithm, - @Param(description = "Amount of Tests") int amount) { + @Param(description = "Amount of Tests") int amount, + @Param(description = "Is versioned", defaultValue = "false") boolean versioned) { if (!IrisToolbelt.isIrisWorld(world)) { sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", Bukkit.getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); return; @@ -289,7 +290,7 @@ public class CommandDeveloper implements DecreeExecutor { service.submit(() -> { try { CountingDataInputStream raw = CountingDataInputStream.wrap(new FileInputStream(file)); - TectonicPlate plate = new TectonicPlate(height, raw); + TectonicPlate plate = new TectonicPlate(height, raw, versioned); raw.close(); double d1 = 0; @@ -308,7 +309,7 @@ public class CommandDeveloper implements DecreeExecutor { size = tmp.length(); start = System.currentTimeMillis(); CountingDataInputStream din = createInput(tmp, algorithm); - new TectonicPlate(height, din); + new TectonicPlate(height, din, true); din.close(); d2 += System.currentTimeMillis() - start; tmp.delete(); diff --git a/core/src/main/java/com/volmit/iris/util/atomics/AtomicAverage.java b/core/src/main/java/com/volmit/iris/util/atomics/AtomicAverage.java index cf9da6d1c..f3574db1d 100644 --- a/core/src/main/java/com/volmit/iris/util/atomics/AtomicAverage.java +++ b/core/src/main/java/com/volmit/iris/util/atomics/AtomicAverage.java @@ -31,11 +31,11 @@ import com.volmit.iris.util.data.DoubleArrayUtils; */ public class AtomicAverage { protected final AtomicDoubleArray values; - protected int cursor; - private double average; - private double lastSum; - private boolean dirty; - private boolean brandNew; + protected transient int cursor; + private transient double average; + private transient double lastSum; + private transient boolean dirty; + private transient boolean brandNew; /** * Create an average holder @@ -57,7 +57,7 @@ public class AtomicAverage { * * @param i the value */ - public void put(double i) { + public synchronized void put(double i) { try { dirty = true; diff --git a/core/src/main/java/com/volmit/iris/util/collection/KSet.java b/core/src/main/java/com/volmit/iris/util/collection/KSet.java index ad96f7085..53b602f9a 100644 --- a/core/src/main/java/com/volmit/iris/util/collection/KSet.java +++ b/core/src/main/java/com/volmit/iris/util/collection/KSet.java @@ -18,29 +18,67 @@ package com.volmit.iris.util.collection; -import java.util.Collection; -import java.util.HashSet; +import org.jetbrains.annotations.NotNull; -public class KSet extends HashSet { +import java.io.Serializable; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; + +public class KSet extends AbstractSet implements Serializable { private static final long serialVersionUID = 1L; + private final ConcurrentHashMap map; public KSet() { - super(); + map = new ConcurrentHashMap<>(); } public KSet(Collection c) { - super(c); + this(); + addAll(c); } public KSet(int initialCapacity, float loadFactor) { - super(initialCapacity, loadFactor); + map = new ConcurrentHashMap<>(initialCapacity, loadFactor); } public KSet(int initialCapacity) { - super(initialCapacity); + map = new ConcurrentHashMap<>(initialCapacity); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean contains(Object o) { + return map.containsKey(o); + } + + @Override + public boolean add(T t) { + return map.putIfAbsent(t, Boolean.TRUE) == null; + } + + @Override + public boolean remove(Object o) { + return map.remove(o) != null; + } + + @Override + public void clear() { + map.clear(); + } + + @NotNull + @Override + public Iterator iterator() { + return map.keySet().iterator(); } public KSet copy() { - return new KSet(this); + return new KSet<>(this); } } diff --git a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java index 3ae6ad880..59ba16399 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -103,7 +103,7 @@ public class Mantle { * @return the file */ public static File fileForRegion(File folder, int x, int z) { - return fileForRegion(folder, key(x, z)); + return fileForRegion(folder, key(x, z), true); } /** @@ -113,12 +113,28 @@ public class Mantle { * @param key the region key * @return the file */ - public static File fileForRegion(File folder, Long key) { - File f = new File(folder, "p." + key + ".ttp.lz4b"); - if (!f.getParentFile().exists()) { - f.getParentFile().mkdirs(); + public static File fileForRegion(File folder, Long key, boolean convert) { + File f = oldFileForRegion(folder, key); + File fv = new File(folder, "pv." + key + ".ttp.lz4b"); + if (f.exists() && !fv.exists() && convert) + return f; + + if (!fv.getParentFile().exists()) { + fv.getParentFile().mkdirs(); } - return f; + return fv; + } + + + /** + * Get the old file for the given region + * + * @param folder the data folder + * @param key the region key + * @return the file + */ + public static File oldFileForRegion(File folder, Long key) { + return new File(folder, "p." + key + ".ttp.lz4b"); } /** @@ -210,7 +226,7 @@ public class Mantle { @RegionCoordinates public boolean hasTectonicPlate(int x, int z) { Long k = key(x, z); - return loadedRegions.containsKey(k) || fileForRegion(dataFolder, k).exists(); + return loadedRegions.containsKey(k) || fileForRegion(dataFolder, k, true).exists(); } /** @@ -364,7 +380,8 @@ public class Mantle { loadedRegions.forEach((i, plate) -> b.queue(() -> { try { plate.close(); - plate.write(fileForRegion(dataFolder, i)); + plate.write(fileForRegion(dataFolder, i, false)); + oldFileForRegion(dataFolder, i).delete(); } catch (Throwable e) { Iris.error("Failed to write Tectonic Plate " + C.DARK_GREEN + Cache.keyX(i) + " " + Cache.keyZ(i)); e.printStackTrace(); @@ -479,7 +496,8 @@ public class Mantle { return; } try { - m.write(fileForRegion(dataFolder, id)); + m.write(fileForRegion(dataFolder, id, false)); + oldFileForRegion(dataFolder, id).delete(); loadedRegions.remove(id); lastUse.remove(id); if (disableClear) toUnload.remove(id); @@ -577,7 +595,7 @@ public class Mantle { if (file.exists()) { try { Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath()); - region = TectonicPlate.read(worldHeight, file); + region = TectonicPlate.read(worldHeight, file, file.getName().startsWith("pv.")); if (region.getX() != x || region.getZ() != z) { Iris.warn("Loaded Tectonic Plate " + x + "," + z + " but read it as " + region.getX() + "," + region.getZ() + "... Assuming " + x + "," + z); diff --git a/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java b/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java index 0cfea94d8..ae4fd1701 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java @@ -19,6 +19,7 @@ package com.volmit.iris.util.mantle; import com.volmit.iris.Iris; +import com.volmit.iris.util.data.Varint; import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.function.Consumer4; import com.volmit.iris.util.io.CountingDataInputStream; @@ -74,11 +75,12 @@ public class MantleChunk { * @throws IOException shit happens * @throws ClassNotFoundException shit happens */ - public MantleChunk(int sectionHeight, CountingDataInputStream din) throws IOException { + public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException { this(sectionHeight, din.readByte(), din.readByte()); int s = din.readByte(); + int l = version < 0 ? flags.length() : Varint.readUnsignedVarInt(din); - for (int i = 0; i < flags.length(); i++) { + for (int i = 0; i < flags.length() && i < l; i++) { flags.set(i, din.readBoolean() ? 1 : 0); } @@ -87,6 +89,10 @@ public class MantleChunk { long size = din.readInt(); if (size == 0) continue; long start = din.count(); + if (i >= sectionHeight) { + din.skipTo(start + size); + continue; + } try { sections.set(i, Matter.readDin(din)); @@ -210,6 +216,7 @@ public class MantleChunk { dos.writeByte(x); dos.writeByte(z); dos.writeByte(sections.length()); + Varint.writeUnsignedVarInt(flags.length(), dos); for (int i = 0; i < flags.length(); i++) { dos.writeBoolean(flags.get(i) == 1); diff --git a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java index e635d4649..82c259aaf 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java @@ -19,9 +19,10 @@ package com.volmit.iris.util.mantle; import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; import com.volmit.iris.engine.EnginePanic; import com.volmit.iris.engine.data.cache.Cache; -import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.data.Varint; import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; @@ -44,7 +45,9 @@ import java.util.concurrent.atomic.AtomicReferenceArray; * Tectonic Plates are fully atomic & thread safe */ public class TectonicPlate { - private static final KSet errors = new KSet<>(); + private static final ThreadLocal errors = ThreadLocal.withInitial(() -> false); + public static final int MISSING = -1; + public static final int CURRENT = 0; private final int sectionHeight; private final AtomicReferenceArray chunks; @@ -74,11 +77,12 @@ public class TectonicPlate { * @param din the data input * @throws IOException shit happens yo */ - public TectonicPlate(int worldHeight, CountingDataInputStream din) throws IOException { + public TectonicPlate(int worldHeight, CountingDataInputStream din, boolean versioned) throws IOException { this(worldHeight, din.readInt(), din.readInt()); if (!din.markSupported()) throw new IOException("Mark not supported!"); + int v = versioned ? Varint.readUnsignedVarInt(din) : MISSING; for (int i = 0; i < chunks.length(); i++) { long size = din.readInt(); if (size == 0) continue; @@ -86,7 +90,7 @@ public class TectonicPlate { try { Iris.addPanic("read-chunk", "Chunk[" + i + "]"); - chunks.set(i, new MantleChunk(sectionHeight, din)); + chunks.set(i, new MantleChunk(v, sectionHeight, din)); EnginePanic.saveLast(); } catch (Throwable e) { long end = start + size; @@ -103,7 +107,7 @@ public class TectonicPlate { } } - public static TectonicPlate read(int worldHeight, File file) throws IOException { + public static TectonicPlate read(int worldHeight, File file, boolean versioned) throws IOException { try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) { fc.lock(); @@ -111,10 +115,10 @@ public class TectonicPlate { LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin); BufferedInputStream bis = new BufferedInputStream(lz4); try (CountingDataInputStream din = CountingDataInputStream.wrap(bis)) { - return new TectonicPlate(worldHeight, din); + return new TectonicPlate(worldHeight, din, versioned); } } finally { - if (errors.remove(Thread.currentThread())) { + if (IrisSettings.get().getGeneral().isDumpMantleOnError() && errors.get()) { File dump = Iris.instance.getDataFolder("dump", file.getName() + ".bin"); try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) { fc.lock(); @@ -124,6 +128,7 @@ public class TectonicPlate { Files.copy(lz4, dump.toPath(), StandardCopyOption.REPLACE_EXISTING); } } + errors.remove(); } } @@ -222,7 +227,7 @@ public class TectonicPlate { write(dos); } Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); - Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName().split("\\Q.\\E")[0] + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); + Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); temp.delete(); } @@ -235,6 +240,7 @@ public class TectonicPlate { public void write(DataOutputStream dos) throws IOException { dos.writeInt(x); dos.writeInt(z); + Varint.writeUnsignedVarInt(CURRENT, dos); var bytes = new ByteArrayOutputStream(8192); var sub = new DataOutputStream(bytes); @@ -256,6 +262,6 @@ public class TectonicPlate { } public static void addError() { - errors.add(Thread.currentThread()); + errors.set(true); } } diff --git a/core/src/main/java/com/volmit/iris/util/matter/Matter.java b/core/src/main/java/com/volmit/iris/util/matter/Matter.java index c04fe2485..086b397bd 100644 --- a/core/src/main/java/com/volmit/iris/util/matter/Matter.java +++ b/core/src/main/java/com/volmit/iris/util/matter/Matter.java @@ -154,15 +154,16 @@ public interface Matter { matter.putSlice(type, slice); } catch (Throwable e) { long end = start + size; - Iris.error("Failed to read matter slice, skipping it."); - Iris.addPanic("read.byte.range", start + " " + end); - Iris.addPanic("read.byte.current", din.count() + ""); - Iris.reportError(e); - e.printStackTrace(); - Iris.panic(); - + if (!(e instanceof ClassNotFoundException)) { + Iris.error("Failed to read matter slice, skipping it."); + Iris.addPanic("read.byte.range", start + " " + end); + Iris.addPanic("read.byte.current", din.count() + ""); + Iris.reportError(e); + e.printStackTrace(); + Iris.panic(); + TectonicPlate.addError(); + } din.skipTo(end); - TectonicPlate.addError(); } } From 2452a2f6338f30af9be46bb847b7de02c315a08d Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 27 May 2025 16:23:02 +0200 Subject: [PATCH 035/138] cleanup of the mantle trimmer / EngineSVC --- .../com/volmit/iris/core/IrisSettings.java | 9 + .../iris/core/commands/CommandDeveloper.java | 50 +-- .../iris/core/service/IrisEngineSVC.java | 382 +++++++----------- .../iris/engine/mantle/EngineMantle.java | 12 +- .../engine/platform/BukkitChunkGenerator.java | 24 +- .../com/volmit/iris/util/mantle/Mantle.java | 150 ++++--- 6 files changed, 244 insertions(+), 383 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 1a86aad11..655967aad 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -23,6 +23,7 @@ import com.volmit.iris.Iris; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.json.JSONException; import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.misc.getHardware; import com.volmit.iris.util.plugin.VolmitSender; import lombok.AllArgsConstructor; import lombok.Data; @@ -160,6 +161,14 @@ public class IrisSettings { public int resourceLoaderCacheSize = 1_024; public int objectLoaderCacheSize = 4_096; public int scriptLoaderCacheSize = 512; + public int tectonicPlateSize = -1; + + public int getTectonicPlateSize() { + if (tectonicPlateSize > 0) + return tectonicPlateSize; + + return (int) (getHardware.getProcessMemory() / 200L); + } } @Data diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java index d54a97e5b..5d6090b00 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java @@ -20,7 +20,6 @@ package com.volmit.iris.core.commands; import com.volmit.iris.Iris; import com.volmit.iris.core.ServerConfigurator; -import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.core.service.IrisEngineSVC; import com.volmit.iris.core.tools.IrisPackBenchmarking; @@ -68,53 +67,8 @@ public class CommandDeveloper implements DecreeExecutor { @Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true) public void EngineStatus() { - List IrisWorlds = new ArrayList<>(); - int TotalLoadedChunks = 0; - int TotalQueuedTectonicPlates = 0; - int TotalNotQueuedTectonicPlates = 0; - int TotalTectonicPlates = 0; - - long lowestUnloadDuration = 0; - long highestUnloadDuration = 0; - - for (World world : Bukkit.getWorlds()) { - try { - if (IrisToolbelt.access(world).getEngine() != null) { - IrisWorlds.add(world); - } - } catch (Exception e) { - // no - } - } - - for (World world : IrisWorlds) { - Engine engine = IrisToolbelt.access(world).getEngine(); - TotalQueuedTectonicPlates += (int) engine.getMantle().getToUnload(); - TotalNotQueuedTectonicPlates += (int) engine.getMantle().getNotQueuedLoadedRegions(); - TotalTectonicPlates += engine.getMantle().getLoadedRegionCount(); - if (highestUnloadDuration <= (long) engine.getMantle().getTectonicDuration()) { - highestUnloadDuration = (long) engine.getMantle().getTectonicDuration(); - } - if (lowestUnloadDuration >= (long) engine.getMantle().getTectonicDuration()) { - lowestUnloadDuration = (long) engine.getMantle().getTectonicDuration(); - } - for (Chunk chunk : world.getLoadedChunks()) { - if (chunk.isLoaded()) { - TotalLoadedChunks++; - } - } - } - Iris.info("-------------------------"); - Iris.info(C.DARK_PURPLE + "Engine Status"); - Iris.info(C.DARK_PURPLE + "Total Loaded Chunks: " + C.LIGHT_PURPLE + TotalLoadedChunks); - Iris.info(C.DARK_PURPLE + "Tectonic Limit: " + C.LIGHT_PURPLE + IrisEngineSVC.getTectonicLimit()); - Iris.info(C.DARK_PURPLE + "Tectonic Total Plates: " + C.LIGHT_PURPLE + TotalTectonicPlates); - Iris.info(C.DARK_PURPLE + "Tectonic Active Plates: " + C.LIGHT_PURPLE + TotalNotQueuedTectonicPlates); - Iris.info(C.DARK_PURPLE + "Tectonic ToUnload: " + C.LIGHT_PURPLE + TotalQueuedTectonicPlates); - Iris.info(C.DARK_PURPLE + "Lowest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(lowestUnloadDuration)); - Iris.info(C.DARK_PURPLE + "Highest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(highestUnloadDuration)); - Iris.info(C.DARK_PURPLE + "Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize())); - Iris.info("-------------------------"); + Iris.service(IrisEngineSVC.class) + .engineStatus(sender()); } @Decree(description = "Test") diff --git a/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java index 2220ccc26..e6c2e364f 100644 --- a/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java @@ -1,317 +1,245 @@ package com.volmit.iris.core.service; +import com.google.common.util.concurrent.AtomicDouble; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.nms.container.Pair; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.platform.PlatformChunkGenerator; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; -import com.volmit.iris.util.mantle.TectonicPlate; -import com.volmit.iris.util.misc.getHardware; import com.volmit.iris.util.plugin.IrisService; -import com.volmit.iris.util.scheduling.ChronoLatch; +import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.scheduling.Looper; -import com.volmit.iris.util.scheduling.PrecisionStopwatch; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.event.EventHandler; -import org.bukkit.event.server.PluginDisableEvent; -import org.bukkit.event.server.ServerLoadEvent; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldUnloadEvent; -import org.checkerframework.checker.units.qual.A; -import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; public class IrisEngineSVC implements IrisService { - public static IrisEngineSVC instance; - public boolean isServerShuttingDown = false; - public boolean isServerLoaded = false; - private static final AtomicInteger tectonicLimit = new AtomicInteger(30); - private ReentrantLock lastUseLock; - private KMap lastUse; - private List IrisWorlds; - private Looper cacheTicker; + private final AtomicInteger tectonicLimit = new AtomicInteger(30); + private final AtomicInteger tectonicPlates = new AtomicInteger(); + private final AtomicInteger queuedTectonicPlates = new AtomicInteger(); + private final AtomicDouble maxIdleDuration = new AtomicDouble(); + private final AtomicDouble minIdleDuration = new AtomicDouble(); + private final AtomicLong loadedChunks = new AtomicLong(); + private final List> worlds = new CopyOnWriteArrayList<>(); private Looper trimTicker; private Looper unloadTicker; private Looper updateTicker; - private PrecisionStopwatch trimAlive; - private PrecisionStopwatch unloadAlive; - public PrecisionStopwatch trimActiveAlive; - public PrecisionStopwatch unloadActiveAlive; - private AtomicInteger TotalTectonicPlates; - private AtomicInteger TotalQueuedTectonicPlates; - private AtomicInteger TotalNotQueuedTectonicPlates; - private AtomicBoolean IsUnloadAlive; - private AtomicBoolean IsTrimAlive; - ChronoLatch cl; - - public List corruptedIrisWorlds = new ArrayList<>(); @Override public void onEnable() { - this.cl = new ChronoLatch(5000); - lastUse = new KMap<>(); - lastUseLock = new ReentrantLock(); - IrisWorlds = new ArrayList<>(); - IsUnloadAlive = new AtomicBoolean(true); - IsTrimAlive = new AtomicBoolean(true); - trimActiveAlive = new PrecisionStopwatch(); - unloadActiveAlive = new PrecisionStopwatch(); - trimAlive = new PrecisionStopwatch(); - unloadAlive = new PrecisionStopwatch(); - TotalTectonicPlates = new AtomicInteger(); - TotalQueuedTectonicPlates = new AtomicInteger(); - TotalNotQueuedTectonicPlates = new AtomicInteger(); - tectonicLimit.set(2); - long t = getHardware.getProcessMemory(); - while (t > 200) { - tectonicLimit.getAndAdd(1); - t = t - 200; + tectonicLimit.set(IrisSettings.get().getPerformance().getTectonicPlateSize()); + for (World world : Bukkit.getWorlds()) { + var access = IrisToolbelt.access(world); + if (access == null) return; + worlds.add(new Pair<>(world, access)); } - this.setup(); - this.TrimLogic(); - this.UnloadLogic(); - - trimAlive.begin(); - unloadAlive.begin(); - trimActiveAlive.begin(); - unloadActiveAlive.begin(); - - updateTicker.start(); - cacheTicker.start(); - //trimTicker.start(); - //unloadTicker.start(); - instance = this; + trimLogic(); + unloadLogic(); + setup(); } - public void engineStatus() { - boolean trimAlive = trimTicker.isAlive(); - boolean unloadAlive = unloadTicker.isAlive(); - Iris.info("Status:"); - Iris.info("- Trim: " + trimAlive); - Iris.info("- Unload: " + unloadAlive); - + @Override + public void onDisable() { + updateTicker.interrupt(); + trimTicker.interrupt(); + unloadTicker.interrupt(); + worlds.clear(); } - public static int getTectonicLimit() { - return tectonicLimit.get(); + public void engineStatus(VolmitSender sender) { + sender.sendMessage(C.DARK_PURPLE + "-------------------------"); + sender.sendMessage(C.DARK_PURPLE + "Status:"); + sender.sendMessage(C.DARK_PURPLE + "- Trim: " + C.LIGHT_PURPLE + trimTicker.isAlive()); + sender.sendMessage(C.DARK_PURPLE + "- Unload: " + C.LIGHT_PURPLE + unloadTicker.isAlive()); + sender.sendMessage(C.DARK_PURPLE + "- Update: " + C.LIGHT_PURPLE + updateTicker.isAlive()); + sender.sendMessage(C.DARK_PURPLE + "Tectonic Plates:"); + sender.sendMessage(C.DARK_PURPLE + "- Limit: " + C.LIGHT_PURPLE + tectonicLimit.get()); + sender.sendMessage(C.DARK_PURPLE + "- Total: " + C.LIGHT_PURPLE + tectonicPlates.get()); + sender.sendMessage(C.DARK_PURPLE + "- Queued: " + C.LIGHT_PURPLE + queuedTectonicPlates.get()); + sender.sendMessage(C.DARK_PURPLE + "- Max Idle Duration: " + C.LIGHT_PURPLE + Form.duration(maxIdleDuration.get(), 2)); + sender.sendMessage(C.DARK_PURPLE + "- Min Idle Duration: " + C.LIGHT_PURPLE + Form.duration(minIdleDuration.get(), 2)); + sender.sendMessage(C.DARK_PURPLE + "Other:"); + sender.sendMessage(C.DARK_PURPLE + "- Iris Worlds: " + C.LIGHT_PURPLE + worlds.size()); + sender.sendMessage(C.DARK_PURPLE + "- Loaded Chunks: " + C.LIGHT_PURPLE + loadedChunks.get()); + sender.sendMessage(C.DARK_PURPLE + "- Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize())); + sender.sendMessage(C.DARK_PURPLE + "-------------------------"); } @EventHandler public void onWorldUnload(WorldUnloadEvent event) { - updateWorlds(); + worlds.removeIf(p -> p.getA() == event.getWorld()); } @EventHandler public void onWorldLoad(WorldLoadEvent event) { - updateWorlds(); + var world = event.getWorld(); + var access = IrisToolbelt.access(world); + if (access == null) return; + worlds.add(new Pair<>(world, access)); } - @EventHandler - public void onServerBoot(ServerLoadEvent event) { - isServerLoaded = true; - } - - @EventHandler - public void onPluginDisable(PluginDisableEvent event) { - if (event.getPlugin().equals(Iris.instance)) { - isServerShuttingDown = true; - } - } - - public void updateWorlds() { - for (World world : Bukkit.getWorlds()) { - try { - if (IrisToolbelt.access(world).getEngine() != null) { - IrisWorlds.add(world); - } - } catch (Exception e) { - // no - } - } - } - - private void setup() { - cacheTicker = new Looper() { - @Override - protected long loop() { - long now = System.currentTimeMillis(); - lastUseLock.lock(); - try { - for (World key : new ArrayList<>(lastUse.keySet())) { - Long last = lastUse.get(key); - if (last == null) - continue; - if (now - last > 60000) { - lastUse.remove(key); - } - } - } finally { - lastUseLock.unlock(); - } - return 1000; - } - }; + private synchronized void setup() { + if (updateTicker != null && updateTicker.isAlive()) + return; updateTicker = new Looper() { @Override protected long loop() { try { - TotalQueuedTectonicPlates.set(0); - TotalNotQueuedTectonicPlates.set(0); - TotalTectonicPlates.set(0); - for (World world : IrisWorlds) { - Engine engine = Objects.requireNonNull(IrisToolbelt.access(world)).getEngine(); - TotalQueuedTectonicPlates.addAndGet((int) engine.getMantle().getToUnload()); - TotalNotQueuedTectonicPlates.addAndGet((int) engine.getMantle().getNotQueuedLoadedRegions()); - TotalTectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount()); - } - if (!isServerShuttingDown && isServerLoaded) { - if (!trimTicker.isAlive()) { - Iris.info(C.RED + "TrimTicker found dead! Booting it up!"); - try { - TrimLogic(); - } catch (Exception e) { - Iris.error("What happened?"); - e.printStackTrace(); - } - } + queuedTectonicPlates.set(0); + tectonicPlates.set(0); + loadedChunks.set(0); - if (!unloadTicker.isAlive()) { - Iris.info(C.RED + "UnloadTicker found dead! Booting it up!"); - try { - UnloadLogic(); - } catch (Exception e) { - Iris.error("What happened?"); - e.printStackTrace(); - } + double maxDuration = Long.MIN_VALUE; + double minDuration = Long.MAX_VALUE; + for (var pair : worlds) { + var engine = pair.getB().getEngine(); + if (engine == null) continue; + + queuedTectonicPlates.addAndGet((int) engine.getMantle().getUnloadRegionCount()); + tectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount()); + loadedChunks.addAndGet(pair.getA().getLoadedChunks().length); + + double duration = engine.getMantle().getAdjustedIdleDuration(); + if (duration > maxDuration) maxDuration = duration; + if (duration < minDuration) minDuration = duration; + } + maxIdleDuration.set(maxDuration); + minIdleDuration.set(minDuration); + + if (!trimTicker.isAlive()) { + Iris.error("TrimTicker found dead! Booting it up!"); + try { + trimLogic(); + } catch (Exception e) { + Iris.error("What happened?"); + e.printStackTrace(); } } - } catch (Exception e) { - return -1; + if (!unloadTicker.isAlive()) { + Iris.error("UnloadTicker found dead! Booting it up!"); + try { + unloadLogic(); + } catch (Exception e) { + Iris.error("What happened?"); + e.printStackTrace(); + } + } + } catch (Throwable e) { + e.printStackTrace(); } return 1000; } }; + updateTicker.start(); } - public void TrimLogic() { - if (trimTicker == null || !trimTicker.isAlive()) { - trimTicker = new Looper() { - private final Supplier supplier = createSupplier(); - @Override - protected long loop() { - long start = System.currentTimeMillis(); - trimAlive.reset(); - try { - Engine engine = supplier.get(); - if (engine != null) { - engine.getMantle().trim(tectonicLimit.get() / lastUse.size()); - } - } catch (Throwable e) { - Iris.reportError(e); - Iris.info(C.RED + "EngineSVC: Failed to trim."); - e.printStackTrace(); - return -1; + private synchronized void trimLogic() { + if (trimTicker != null && trimTicker.isAlive()) + return; + + trimTicker = new Looper() { + private final Supplier supplier = createSupplier(); + + @Override + protected long loop() { + long start = System.currentTimeMillis(); + try { + Engine engine = supplier.get(); + if (engine != null) { + engine.getMantle().trim(tectonicLimit.get() / worlds.size()); } - - int size = lastUse.size(); - long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start); - if (time <= 0) - return 0; - return time; + } catch (Throwable e) { + Iris.reportError(e); + Iris.error("EngineSVC: Failed to trim."); + e.printStackTrace(); + return -1; } - }; - trimTicker.start(); - } + + int size = worlds.size(); + long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start); + if (time <= 0) + return 0; + return time; + } + }; + trimTicker.start(); } - public void UnloadLogic() { - if (unloadTicker == null || !unloadTicker.isAlive()) { - unloadTicker = new Looper() { - private final Supplier supplier = createSupplier(); - @Override - protected long loop() { - long start = System.currentTimeMillis(); - unloadAlive.reset(); - try { - Engine engine = supplier.get(); - if (engine != null) { - long unloadStart = System.currentTimeMillis(); - int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / lastUse.size()); - if (count > 0) { - Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2)); - } + private synchronized void unloadLogic() { + if (unloadTicker != null && unloadTicker.isAlive()) + return; + + unloadTicker = new Looper() { + private final Supplier supplier = createSupplier(); + + @Override + protected long loop() { + long start = System.currentTimeMillis(); + try { + Engine engine = supplier.get(); + if (engine != null) { + long unloadStart = System.currentTimeMillis(); + int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / worlds.size()); + if (count > 0) { + Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2)); } - } catch (Throwable e) { - Iris.reportError(e); - Iris.info(C.RED + "EngineSVC: Failed to unload."); - e.printStackTrace(); - return -1; } - - int size = lastUse.size(); - long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start); - if (time <= 0) - return 0; - return time; + } catch (Throwable e) { + Iris.reportError(e); + Iris.error("EngineSVC: Failed to unload."); + e.printStackTrace(); + return -1; } - }; - unloadTicker.start(); - } + + int size = worlds.size(); + long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start); + if (time <= 0) + return 0; + return time; + } + }; + unloadTicker.start(); } private Supplier createSupplier() { AtomicInteger i = new AtomicInteger(); return () -> { - List worlds = Bukkit.getWorlds(); if (i.get() >= worlds.size()) { i.set(0); } try { for (int j = 0; j < worlds.size(); j++) { - World world = worlds.get(i.getAndIncrement()); - PlatformChunkGenerator generator = IrisToolbelt.access(world); + var pair = worlds.get(i.getAndIncrement()); if (i.get() >= worlds.size()) { i.set(0); } - if (generator != null) { - Engine engine = generator.getEngine(); - boolean closed = engine.getMantle().getData().isClosed(); - if (engine != null && !engine.isStudio() && !closed) { - lastUseLock.lock(); - lastUse.put(world, System.currentTimeMillis()); - lastUseLock.unlock(); - return engine; - } + var engine = pair.getB().getEngine(); + if (engine != null && !engine.isClosed() && engine.getMantle().getMantle().shouldReduce(engine)) { + return engine; } } } catch (Throwable e) { - Iris.info(C.RED + "EngineSVC: Failed to create supplier."); + Iris.error("EngineSVC: Failed to create supplier."); e.printStackTrace(); Iris.reportError(e); } return null; }; } - - @Override - public void onDisable() { - cacheTicker.interrupt(); - trimTicker.interrupt(); - unloadTicker.interrupt(); - lastUse.clear(); - } } diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java index b55ce9e7f..d581fecaa 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java @@ -299,13 +299,11 @@ public interface EngineMantle extends IObjectPlacer { } } - default long getToUnload(){ - return getMantle().getToUnload().size(); + default long getUnloadRegionCount() { + return getMantle().getUnloadRegionCount(); } - default long getNotQueuedLoadedRegions(){ - return getMantle().getLoadedRegions().size() - getMantle().getToUnload().size(); - } - default double getTectonicDuration(){ - return getMantle().getAdjustedIdleDuration().get(); + + default double getAdjustedIdleDuration() { + return getMantle().getAdjustedIdleDuration(); } } \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index c2ed23c83..410e8bbe0 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -86,12 +86,12 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun private final boolean studio; private final AtomicInteger a = new AtomicInteger(0); private final CompletableFuture spawnChunks = new CompletableFuture<>(); - private Engine engine; - private Looper hotloader; - private StudioMode lastMode; - private DummyBiomeProvider dummyBiomeProvider; + private volatile Engine engine; + private volatile Looper hotloader; + private volatile StudioMode lastMode; + private volatile DummyBiomeProvider dummyBiomeProvider; @Setter - private StudioGenerator studioGenerator; + private volatile StudioGenerator studioGenerator; private boolean initialized = false; @@ -110,20 +110,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance); } - private static Field getField(Class clazz, String fieldName) - throws NoSuchFieldException { - try { - return clazz.getDeclaredField(fieldName); - } catch (NoSuchFieldException e) { - Class superClass = clazz.getSuperclass(); - if (superClass == null) { - throw e; - } else { - return getField(superClass, fieldName); - } - } - } - @EventHandler(priority = EventPriority.LOWEST) public void onWorldInit(WorldInitEvent event) { try { diff --git a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java index 59ba16399..41fa887f0 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -21,14 +21,13 @@ package com.volmit.iris.util.mantle; import com.google.common.util.concurrent.AtomicDouble; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; -import com.volmit.iris.core.service.IrisEngineSVC; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.mantle.EngineMantle; import com.volmit.iris.engine.mantle.MantleWriter; -import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.documentation.RegionCoordinates; @@ -51,8 +50,6 @@ import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.ReentrantLock; /** * The mantle can store any type of data slice anywhere and manage regions & IO on it's own. @@ -60,18 +57,18 @@ import java.util.concurrent.locks.ReentrantLock; */ public class Mantle { - private static final boolean disableClear = System.getProperty("disableClear", "false").equals("true"); private final File dataFolder; @Getter private final int worldHeight; private final Map lastUse; - @Getter private final Map loadedRegions; private final HyperLock hyperLock; private final AtomicBoolean closed; private final MultiBurst ioBurst; private final AtomicBoolean ioTrim; private final AtomicBoolean ioTectonicUnload; + private final AtomicDouble adjustedIdleDuration; + private final KSet toUnload; /** * Create a new mantle @@ -91,6 +88,8 @@ public class Mantle { loadedRegions = new KMap<>(); lastUse = new KMap<>(); ioBurst = MultiBurst.burst; + adjustedIdleDuration = new AtomicDouble(0); + toUnload = new KSet<>(); Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath()); } @@ -410,16 +409,6 @@ public class Mantle { return numberOfEntries * bytesPerEntry; } - @Getter - private final AtomicDouble adjustedIdleDuration = new AtomicDouble(0); - @Getter - private final AtomicInteger forceAggressiveThreshold = new AtomicInteger(30); - @Getter - private final AtomicLong oldestTectonicPlate = new AtomicLong(0); - private final ReentrantLock unloadLock = new ReentrantLock(); - @Getter - private final KList toUnload = new KList<>(); - /** * Save & unload regions that have not been used for more than the * specified amount of milliseconds @@ -441,85 +430,74 @@ public class Mantle { } ioTrim.set(true); - unloadLock.lock(); try { - if (lastUse != null && IrisEngineSVC.instance != null) { - if (!lastUse.isEmpty()) { - Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration.get(), 0)); - for (long i : new ArrayList<>(lastUse.keySet())) { - double finalAdjustedIdleDuration = adjustedIdleDuration.get(); - hyperLock.withLong(i, () -> { - Long lastUseTime = lastUse.get(i); - if (lastUseTime != null && M.ms() - lastUseTime >= finalAdjustedIdleDuration) { - toUnload.add(i); - Iris.debug("Tectonic Region added to unload"); - IrisEngineSVC.instance.trimActiveAlive.reset(); - } - }); - } - } - } + double adjustedIdleDuration = this.adjustedIdleDuration.get(); + Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration, 0)); + if (lastUse.isEmpty()) return; + double unloadTime = M.ms() - adjustedIdleDuration; + for (long id : lastUse.keySet()) { + hyperLock.withLong(id, () -> { + Long lastUseTime = lastUse.get(id); + if (lastUseTime != null && lastUseTime < unloadTime) { + toUnload.add(id); + Iris.debug("Tectonic Region added to unload"); + } + }); + } } catch (Throwable e) { Iris.reportError(e); } finally { ioTrim.set(false); - unloadLock.unlock(); } } public synchronized int unloadTectonicPlate(int tectonicLimit) { + if (closed.get()) { + throw new RuntimeException("The Mantle is closed"); + } + AtomicInteger i = new AtomicInteger(); - unloadLock.lock(); - BurstExecutor burst = null; - if (IrisEngineSVC.instance != null) { - try { - KList copy = toUnload.copy(); - if (!disableClear) toUnload.clear(); - burst = MultiBurst.burst.burst(copy.size()); - burst.setMulticore(copy.size() > tectonicLimit); - for (int j = 0; j < copy.size(); j++) { - Long id = copy.get(j); - if (id == null) { - Iris.error("Null id in unloadTectonicPlate at index " + j); - continue; + BurstExecutor burst = ioBurst.burst(toUnload.size()); + burst.setMulticore(toUnload.size() > tectonicLimit); + + ioTectonicUnload.set(true); + try { + for (long id : toUnload) { + burst.queue(() -> hyperLock.withLong(id, () -> { + TectonicPlate m = loadedRegions.get(id); + if (m == null) { + Iris.debug("Tectonic Plate was added to unload while not loaded " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id)); + toUnload.remove(id); + return; } - burst.queue(() -> - hyperLock.withLong(id, () -> { - TectonicPlate m = loadedRegions.get(id); - if (m != null) { - if (m.inUse()) { - Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ()); - if (disableClear) toUnload.remove(id); - lastUse.put(id, M.ms()); - return; - } - try { - m.write(fileForRegion(dataFolder, id, false)); - oldFileForRegion(dataFolder, id).delete(); - loadedRegions.remove(id); - lastUse.remove(id); - if (disableClear) toUnload.remove(id); - i.incrementAndGet(); - Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id)); - IrisEngineSVC.instance.unloadActiveAlive.reset(); - } catch (IOException e) { - Iris.reportError(e); - } - } - })); - } - burst.complete(); - } catch (Throwable e) { - e.printStackTrace(); - if (burst != null) - burst.complete(); - } finally { - unloadLock.unlock(); - ioTectonicUnload.set(true); + if (m.inUse()) { + Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ()); + toUnload.remove(id); + return; + } + + try { + m.write(fileForRegion(dataFolder, id, false)); + oldFileForRegion(dataFolder, id).delete(); + loadedRegions.remove(id); + lastUse.remove(id); + toUnload.remove(id); + i.incrementAndGet(); + Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id)); + } catch (IOException e) { + Iris.reportError(e); + } + })); } - return i.get(); + burst.complete(); + } catch (Throwable e) { + Iris.reportError(e); + e.printStackTrace(); + burst.complete(); + } finally { + ioTectonicUnload.set(false); } return i.get(); } @@ -535,7 +513,7 @@ public class Mantle { */ @RegionCoordinates private TectonicPlate get(int x, int z) { - if (ioTrim.get()) { + if (ioTrim.get() || ioTectonicUnload.get()) { try { return getSafe(x, z).get(); } catch (InterruptedException e) { @@ -645,6 +623,14 @@ public class Mantle { return loadedRegions.size(); } + public int getUnloadRegionCount() { + return toUnload.size(); + } + + public double getAdjustedIdleDuration() { + return adjustedIdleDuration.get(); + } + public void set(int x, int y, int z, MatterSlice slice) { if (slice.isEmpty()) { return; From b132862a601065669b5b87fc436f752c171fad69 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 27 May 2025 22:50:30 +0200 Subject: [PATCH 036/138] update last use for in use tectonic plates --- core/src/main/java/com/volmit/iris/util/mantle/Mantle.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java index 41fa887f0..03bd78fe1 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -474,6 +474,7 @@ public class Mantle { if (m.inUse()) { Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ()); + lastUse.put(id, M.ms()); toUnload.remove(id); return; } From f4d1177c51bf47ce8e3bab08a82d05c25a95f7ae Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 28 May 2025 12:57:11 +0200 Subject: [PATCH 037/138] fix cave fluid ignoring fluid palette --- .../com/volmit/iris/engine/modifier/IrisCarveModifier.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/modifier/IrisCarveModifier.java b/core/src/main/java/com/volmit/iris/engine/modifier/IrisCarveModifier.java index 4f15b12ca..a7bb391be 100644 --- a/core/src/main/java/com/volmit/iris/engine/modifier/IrisCarveModifier.java +++ b/core/src/main/java/com/volmit/iris/engine/modifier/IrisCarveModifier.java @@ -43,7 +43,6 @@ import org.bukkit.block.data.BlockData; public class IrisCarveModifier extends EngineAssignedModifier { private final RNG rng; private final BlockData AIR = Material.CAVE_AIR.createBlockData(); - private final BlockData WATER = Material.WATER.createBlockData(); private final BlockData LAVA = Material.LAVA.createBlockData(); private final IrisDecorantActuator decorant; @@ -103,7 +102,7 @@ public class IrisCarveModifier extends EngineAssignedModifier { } if (c.isWater()) { - output.set(rx, yy, rz, WATER); + output.set(rx, yy, rz, context.getFluid().get(rx, rz)); } else if (c.isLava()) { output.set(rx, yy, rz, LAVA); } else { From 3cb9585dd854797a79731ec67faf6d406619f84b Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 28 May 2025 21:48:41 +0200 Subject: [PATCH 038/138] fix pregenerator not closing when a new one is started --- .../volmit/iris/core/gui/PregeneratorJob.java | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java b/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java index e12ca46a6..348b42349 100644 --- a/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java +++ b/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java @@ -43,6 +43,7 @@ import java.awt.image.BufferedImage; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; @@ -55,7 +56,7 @@ public class PregeneratorJob implements PregenListener { private static final Color COLOR_NETWORK_GENERATING = parseColor("#836b8c"); private static final Color COLOR_GENERATED = parseColor("#65c295"); private static final Color COLOR_CLEANED = parseColor("#34eb93"); - public static PregeneratorJob instance; + private static final AtomicReference instance = new AtomicReference<>(); private final MemoryMonitor monitor; private final PregenTask task; private final boolean saving; @@ -73,8 +74,14 @@ public class PregeneratorJob implements PregenListener { private String[] info; public PregeneratorJob(PregenTask task, PregeneratorMethod method, Engine engine) { + instance.updateAndGet(old -> { + if (old != null) { + old.pregenerator.close(); + old.close(); + } + return this; + }); this.engine = engine; - instance = this; monitor = new MemoryMonitor(50); saving = false; info = new String[]{"Initializing..."}; @@ -103,37 +110,40 @@ public class PregeneratorJob implements PregenListener { } public static boolean shutdownInstance() { - if (instance == null) { + PregeneratorJob inst = instance.get(); + if (inst == null) { return false; } - J.a(() -> instance.pregenerator.close()); + J.a(inst.pregenerator::close); return true; } public static PregeneratorJob getInstance() { - return instance; + return instance.get(); } public static boolean pauseResume() { - if (instance == null) { + PregeneratorJob inst = instance.get(); + if (inst == null) { return false; } if (isPaused()) { - instance.pregenerator.resume(); + inst.pregenerator.resume(); } else { - instance.pregenerator.pause(); + inst.pregenerator.pause(); } return true; } public static boolean isPaused() { - if (instance == null) { + PregeneratorJob inst = instance.get(); + if (inst == null) { return true; } - return instance.paused(); + return inst.paused(); } private static Color parseColor(String c) { @@ -183,7 +193,7 @@ public class PregeneratorJob implements PregenListener { J.a(() -> { pregenerator.close(); close(); - instance = null; + instance.compareAndSet(this, null); }); } @@ -311,7 +321,7 @@ public class PregeneratorJob implements PregenListener { @Override public void onClose() { close(); - instance = null; + instance.compareAndSet(this, null); whenDone.forEach(Runnable::run); service.shutdownNow(); } From 22ac9ebf475b03c5f361873b2ccf46d7f48b683f Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Thu, 29 May 2025 14:18:30 +0200 Subject: [PATCH 039/138] update nexo api to release 1.6.0 --- build.gradle | 2 +- core/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b385555b4..ae3965240 100644 --- a/build.gradle +++ b/build.gradle @@ -139,7 +139,7 @@ allprojects { maven { url "https://repo.triumphteam.dev/snapshots" } maven { url "https://repo.mineinabyss.com/releases" } maven { url 'https://hub.jeff-media.com/nexus/repository/jeff-media-public/' } - maven { url "https://repo.nexomc.com/snapshots/" } + maven { url "https://repo.nexomc.com/releases/" } maven { url "https://libraries.minecraft.net" } } diff --git a/core/build.gradle b/core/build.gradle index 2c1e96fc2..9effb5654 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -62,7 +62,7 @@ dependencies { // Third Party Integrations compileOnly 'com.ticxo.playeranimator:PlayerAnimator:R1.2.7' - compileOnly 'com.nexomc:nexo:1.0.0-dev.38' + compileOnly 'com.nexomc:nexo:1.6.0' compileOnly 'com.github.LoneDev6:api-itemsadder:3.4.1-r4' compileOnly 'com.github.PlaceholderAPI:placeholderapi:2.11.3' compileOnly 'com.github.Ssomar-Developement:SCore:4.23.10.8' From ad85a0bbd1efd3cd7105f786a591aedc014a842e Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Thu, 29 May 2025 23:17:23 +0200 Subject: [PATCH 040/138] implement marker exhaustionChance --- .../java/com/volmit/iris/engine/object/IrisEntitySpawn.java | 6 +----- .../main/java/com/volmit/iris/engine/object/IrisMarker.java | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisEntitySpawn.java b/core/src/main/java/com/volmit/iris/engine/object/IrisEntitySpawn.java index c93ffaade..a0eb9500d 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisEntitySpawn.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisEntitySpawn.java @@ -28,7 +28,6 @@ import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.matter.MatterMarker; import com.volmit.iris.util.matter.slices.MarkerMatter; -import io.lumine.mythic.bukkit.adapters.BukkitEntity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -38,9 +37,6 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.LivingEntity; -import org.bukkit.util.BoundingBox; @Snippet("entity-spawn") @Accessors(chain = true) @@ -116,7 +112,7 @@ public class IrisEntitySpawn implements IRare { World world = gen.getWorld().realWorld(); if (spawns > 0) { - if (referenceMarker != null) { + if (referenceMarker != null && referenceMarker.shouldExhaust()) { gen.getMantle().getMantle().remove(c.getX(), c.getY() - gen.getWorld().minHeight(), c.getZ(), MatterMarker.class); } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisMarker.java b/core/src/main/java/com/volmit/iris/engine/object/IrisMarker.java index fc0c7eef9..fc5a3c761 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisMarker.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisMarker.java @@ -51,10 +51,10 @@ public class IrisMarker extends IrisRegistrant { private boolean emptyAbove = true; @Desc("If this marker is used, what is the chance it removes itself. For example 25% (0.25) would mean that on average 4 uses will remove a specific marker. Set this below 0 (-1) to never exhaust & set this to 1 or higher to always exhaust on first use.") - private double exhaustionChance = 0.33; + private double exhaustionChance = 0; public boolean shouldExhaust() { - return RNG.r.chance(exhaustionChance); + return exhaustionChance > RNG.r.nextDouble(); } @Override From adb7188eb93bef35b3923fbc85c31b8513d2b5f1 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 2 Jun 2025 14:46:37 +0200 Subject: [PATCH 041/138] refactor mantle cleanup --- .../java/com/volmit/iris/core/IrisSettings.java | 1 + .../com/volmit/iris/engine/IrisWorldManager.java | 14 +++++++++++--- .../volmit/iris/engine/mantle/EngineMantle.java | 16 ++++++++++------ .../java/com/volmit/iris/util/mantle/Mantle.java | 9 +++------ 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 655967aad..89f1c1015 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -162,6 +162,7 @@ public class IrisSettings { public int objectLoaderCacheSize = 4_096; public int scriptLoaderCacheSize = 512; public int tectonicPlateSize = -1; + public int mantleCleanupDelay = 200; public int getTectonicPlateSize() { if (tectonicPlateSize > 0) diff --git a/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java b/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java index 28e48bd18..95ba698c9 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java @@ -55,6 +55,7 @@ import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.inventory.ItemStack; +import java.lang.ref.WeakReference; import java.util.List; import java.util.Map; import java.util.Set; @@ -422,9 +423,16 @@ public class IrisWorldManager extends EngineAssignedWorldManager { return; } - energy += 0.3; - fixEnergy(); - getEngine().cleanupMantleChunk(e.getX(), e.getZ()); + var ref = new WeakReference<>(e.getWorld()); + int x = e.getX(), z = e.getZ(); + J.s(() -> { + World world = ref.get(); + if (world == null || !world.isChunkLoaded(x, z)) + return; + energy += 0.3; + fixEnergy(); + getEngine().cleanupMantleChunk(x, z); + }, IrisSettings.get().getPerformance().mantleCleanupDelay); if (generated) { //INMS.get().injectBiomesFromMantle(e, getMantle()); diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java index d581fecaa..ba0759c83 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java @@ -289,13 +289,17 @@ public interface EngineMantle extends IObjectPlacer { } default void cleanupChunk(int x, int z) { - if (!getMantle().hasFlag(x, z, MantleFlag.CLEANED) && isCovered(x, z)) { - getMantle().raiseFlag(x, z, MantleFlag.CLEANED, () -> { - getMantle().deleteChunkSlice(x, z, BlockData.class); - getMantle().deleteChunkSlice(x, z, String.class); - getMantle().deleteChunkSlice(x, z, MatterCavern.class); - getMantle().deleteChunkSlice(x, z, MatterFluidBody.class); + if (!isCovered(x, z)) return; + MantleChunk chunk = getMantle().getChunk(x, z).use(); + try { + chunk.raiseFlag(MantleFlag.CLEANED, () -> { + chunk.deleteSlices(BlockData.class); + chunk.deleteSlices(String.class); + chunk.deleteSlices(MatterCavern.class); + chunk.deleteSlices(MatterFluidBody.class); }); + } finally { + chunk.release(); } } diff --git a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java index 03bd78fe1..77b849272 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -421,12 +421,9 @@ public class Mantle { } adjustedIdleDuration.set(baseIdleDuration); - - if (loadedRegions != null) { - if (loadedRegions.size() > tectonicLimit) { - // todo update this correctly and maybe do something when its above a 100% - adjustedIdleDuration.set(Math.max(adjustedIdleDuration.get() - (1000 * (((loadedRegions.size() - tectonicLimit) / (double) tectonicLimit) * 100) * 0.4), 4000)); - } + if (loadedRegions.size() > tectonicLimit) { + // todo update this correctly and maybe do something when its above a 100% + adjustedIdleDuration.set(Math.max(adjustedIdleDuration.get() - (1000 * (((loadedRegions.size() - tectonicLimit) / (double) tectonicLimit) * 100) * 0.4), 4000)); } ioTrim.set(true); From 2c60192de33b2b73aa29299f9ff1f153e5ef1faf Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 2 Jun 2025 17:25:49 +0200 Subject: [PATCH 042/138] use scheduled thread pool instead of loopers for the EngineSVC --- .../com/volmit/iris/core/IrisSettings.java | 11 + .../iris/core/service/IrisEngineSVC.java | 243 +++++++++--------- 2 files changed, 131 insertions(+), 123 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 89f1c1015..b7e5bae20 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -155,6 +155,7 @@ public class IrisSettings { @Data public static class IrisSettingsPerformance { + private IrisSettingsEngineSVC engineSVC = new IrisSettingsEngineSVC(); public boolean trimMantleInStudio = false; public int mantleKeepAlive = 30; public int cacheSize = 4_096; @@ -242,4 +243,14 @@ public class IrisSettings { public boolean disableTimeAndWeather = true; public boolean autoStartDefaultStudio = false; } + + @Data + public static class IrisSettingsEngineSVC { + public boolean useVirtualThreads = true; + public int priority = Thread.NORM_PRIORITY; + + public int getPriority() { + return Math.max(Math.min(priority, Thread.MAX_PRIORITY), Thread.MIN_PRIORITY); + } + } } diff --git a/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java index e6c2e364f..51351f657 100644 --- a/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java @@ -4,67 +4,72 @@ import com.google.common.util.concurrent.AtomicDouble; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.loader.IrisData; -import com.volmit.iris.core.nms.container.Pair; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.platform.PlatformChunkGenerator; +import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.plugin.IrisService; import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.scheduling.Looper; +import lombok.Synchronized; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldUnloadEvent; +import org.jetbrains.annotations.Nullable; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Supplier; public class IrisEngineSVC implements IrisService { private final AtomicInteger tectonicLimit = new AtomicInteger(30); private final AtomicInteger tectonicPlates = new AtomicInteger(); private final AtomicInteger queuedTectonicPlates = new AtomicInteger(); + private final AtomicInteger trimmerAlive = new AtomicInteger(); + private final AtomicInteger unloaderAlive = new AtomicInteger(); + private final AtomicInteger totalWorlds = new AtomicInteger(); private final AtomicDouble maxIdleDuration = new AtomicDouble(); private final AtomicDouble minIdleDuration = new AtomicDouble(); private final AtomicLong loadedChunks = new AtomicLong(); - private final List> worlds = new CopyOnWriteArrayList<>(); - private Looper trimTicker; - private Looper unloadTicker; + private final KMap worlds = new KMap<>(); + private ScheduledExecutorService service; private Looper updateTicker; @Override public void onEnable() { - tectonicLimit.set(IrisSettings.get().getPerformance().getTectonicPlateSize()); - for (World world : Bukkit.getWorlds()) { - var access = IrisToolbelt.access(world); - if (access == null) return; - worlds.add(new Pair<>(world, access)); - } - - trimLogic(); - unloadLogic(); + var settings = IrisSettings.get().getPerformance(); + var engine = settings.getEngineSVC(); + service = Executors.newScheduledThreadPool(0, + (engine.isUseVirtualThreads() + ? Thread.ofVirtual() + : Thread.ofPlatform().priority(engine.getPriority())) + .name("Iris EngineSVC-", 0) + .factory()); + tectonicLimit.set(settings.getTectonicPlateSize()); + Bukkit.getWorlds().forEach(this::add); setup(); } @Override public void onDisable() { + service.shutdown(); updateTicker.interrupt(); - trimTicker.interrupt(); - unloadTicker.interrupt(); + worlds.keySet().forEach(this::remove); worlds.clear(); } public void engineStatus(VolmitSender sender) { sender.sendMessage(C.DARK_PURPLE + "-------------------------"); sender.sendMessage(C.DARK_PURPLE + "Status:"); - sender.sendMessage(C.DARK_PURPLE + "- Trim: " + C.LIGHT_PURPLE + trimTicker.isAlive()); - sender.sendMessage(C.DARK_PURPLE + "- Unload: " + C.LIGHT_PURPLE + unloadTicker.isAlive()); - sender.sendMessage(C.DARK_PURPLE + "- Update: " + C.LIGHT_PURPLE + updateTicker.isAlive()); + sender.sendMessage(C.DARK_PURPLE + "- Service: " + C.LIGHT_PURPLE + (service.isShutdown() ? "Shutdown" : "Running")); + sender.sendMessage(C.DARK_PURPLE + "- Updater: " + C.LIGHT_PURPLE + (updateTicker.isAlive() ? "Running" : "Stopped")); + sender.sendMessage(C.DARK_PURPLE + "- Trimmers: " + C.LIGHT_PURPLE + trimmerAlive.get()); + sender.sendMessage(C.DARK_PURPLE + "- Unloaders: " + C.LIGHT_PURPLE + unloaderAlive.get()); sender.sendMessage(C.DARK_PURPLE + "Tectonic Plates:"); sender.sendMessage(C.DARK_PURPLE + "- Limit: " + C.LIGHT_PURPLE + tectonicLimit.get()); sender.sendMessage(C.DARK_PURPLE + "- Total: " + C.LIGHT_PURPLE + tectonicPlates.get()); @@ -72,7 +77,7 @@ public class IrisEngineSVC implements IrisService { sender.sendMessage(C.DARK_PURPLE + "- Max Idle Duration: " + C.LIGHT_PURPLE + Form.duration(maxIdleDuration.get(), 2)); sender.sendMessage(C.DARK_PURPLE + "- Min Idle Duration: " + C.LIGHT_PURPLE + Form.duration(minIdleDuration.get(), 2)); sender.sendMessage(C.DARK_PURPLE + "Other:"); - sender.sendMessage(C.DARK_PURPLE + "- Iris Worlds: " + C.LIGHT_PURPLE + worlds.size()); + sender.sendMessage(C.DARK_PURPLE + "- Iris Worlds: " + C.LIGHT_PURPLE + totalWorlds.get()); sender.sendMessage(C.DARK_PURPLE + "- Loaded Chunks: " + C.LIGHT_PURPLE + loadedChunks.get()); sender.sendMessage(C.DARK_PURPLE + "- Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize())); sender.sendMessage(C.DARK_PURPLE + "-------------------------"); @@ -80,15 +85,24 @@ public class IrisEngineSVC implements IrisService { @EventHandler public void onWorldUnload(WorldUnloadEvent event) { - worlds.removeIf(p -> p.getA() == event.getWorld()); + remove(event.getWorld()); } @EventHandler public void onWorldLoad(WorldLoadEvent event) { - var world = event.getWorld(); + add(event.getWorld()); + } + + private void remove(World world) { + var entry = worlds.remove(world); + if (entry == null) return; + entry.close(); + } + + private void add(World world) { var access = IrisToolbelt.access(world); if (access == null) return; - worlds.add(new Pair<>(world, access)); + worlds.put(world, new Registered(world.getName(), access)); } private synchronized void setup() { @@ -102,16 +116,26 @@ public class IrisEngineSVC implements IrisService { queuedTectonicPlates.set(0); tectonicPlates.set(0); loadedChunks.set(0); + unloaderAlive.set(0); + trimmerAlive.set(0); + totalWorlds.set(0); double maxDuration = Long.MIN_VALUE; double minDuration = Long.MAX_VALUE; - for (var pair : worlds) { - var engine = pair.getB().getEngine(); + for (var entry : worlds.entrySet()) { + var registered = entry.getValue(); + if (registered.closed) continue; + + totalWorlds.incrementAndGet(); + unloaderAlive.addAndGet(registered.unloaderAlive() ? 1 : 0); + trimmerAlive.addAndGet(registered.trimmerAlive() ? 1 : 0); + + var engine = registered.getEngine(); if (engine == null) continue; queuedTectonicPlates.addAndGet((int) engine.getMantle().getUnloadRegionCount()); tectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount()); - loadedChunks.addAndGet(pair.getA().getLoadedChunks().length); + loadedChunks.addAndGet(entry.getKey().getLoadedChunks().length); double duration = engine.getMantle().getAdjustedIdleDuration(); if (duration > maxDuration) maxDuration = duration; @@ -120,25 +144,7 @@ public class IrisEngineSVC implements IrisService { maxIdleDuration.set(maxDuration); minIdleDuration.set(minDuration); - if (!trimTicker.isAlive()) { - Iris.error("TrimTicker found dead! Booting it up!"); - try { - trimLogic(); - } catch (Exception e) { - Iris.error("What happened?"); - e.printStackTrace(); - } - } - - if (!unloadTicker.isAlive()) { - Iris.error("UnloadTicker found dead! Booting it up!"); - try { - unloadLogic(); - } catch (Exception e) { - Iris.error("What happened?"); - e.printStackTrace(); - } - } + worlds.values().forEach(Registered::update); } catch (Throwable e) { e.printStackTrace(); } @@ -148,98 +154,89 @@ public class IrisEngineSVC implements IrisService { updateTicker.start(); } - private synchronized void trimLogic() { - if (trimTicker != null && trimTicker.isAlive()) - return; + private final class Registered { + private final String name; + private final PlatformChunkGenerator access; + private transient ScheduledFuture trimmer; + private transient ScheduledFuture unloader; + private transient boolean closed; - trimTicker = new Looper() { - private final Supplier supplier = createSupplier(); + private Registered(String name, PlatformChunkGenerator access) { + this.name = name; + this.access = access; + update(); + } - @Override - protected long loop() { - long start = System.currentTimeMillis(); - try { - Engine engine = supplier.get(); - if (engine != null) { + private boolean unloaderAlive() { + return unloader != null && !unloader.isDone() && !unloader.isCancelled(); + } + + private boolean trimmerAlive() { + return trimmer != null && !trimmer.isDone() && !trimmer.isCancelled(); + } + + @Synchronized + private void update() { + if (closed || service == null || service.isShutdown()) + return; + + if (trimmer == null || trimmer.isDone() || trimmer.isCancelled()) { + trimmer = service.scheduleAtFixedRate(() -> { + Engine engine = getEngine(); + if (engine == null || !engine.getMantle().getMantle().shouldReduce(engine)) + return; + + try { engine.getMantle().trim(tectonicLimit.get() / worlds.size()); + } catch (Throwable e) { + Iris.reportError(e); + Iris.error("EngineSVC: Failed to trim for " + name); + e.printStackTrace(); } - } catch (Throwable e) { - Iris.reportError(e); - Iris.error("EngineSVC: Failed to trim."); - e.printStackTrace(); - return -1; - } - - int size = worlds.size(); - long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start); - if (time <= 0) - return 0; - return time; + }, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS); } - }; - trimTicker.start(); - } - private synchronized void unloadLogic() { - if (unloadTicker != null && unloadTicker.isAlive()) - return; + if (unloader == null || unloader.isDone() || unloader.isCancelled()) { + unloader = service.scheduleAtFixedRate(() -> { + Engine engine = getEngine(); + if (engine == null || !engine.getMantle().getMantle().shouldReduce(engine)) + return; - unloadTicker = new Looper() { - private final Supplier supplier = createSupplier(); - - @Override - protected long loop() { - long start = System.currentTimeMillis(); - try { - Engine engine = supplier.get(); - if (engine != null) { + try { long unloadStart = System.currentTimeMillis(); int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / worlds.size()); if (count > 0) { Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2)); } + } catch (Throwable e) { + Iris.reportError(e); + Iris.error("EngineSVC: Failed to unload for " + name); + e.printStackTrace(); } - } catch (Throwable e) { - Iris.reportError(e); - Iris.error("EngineSVC: Failed to unload."); - e.printStackTrace(); - return -1; - } - - int size = worlds.size(); - long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start); - if (time <= 0) - return 0; - return time; + }, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS); } - }; - unloadTicker.start(); - } + } - private Supplier createSupplier() { - AtomicInteger i = new AtomicInteger(); - return () -> { - if (i.get() >= worlds.size()) { - i.set(0); - } - try { - for (int j = 0; j < worlds.size(); j++) { - var pair = worlds.get(i.getAndIncrement()); - if (i.get() >= worlds.size()) { - i.set(0); - } + @Synchronized + private void close() { + if (closed) return; + closed = true; - var engine = pair.getB().getEngine(); - if (engine != null && !engine.isClosed() && engine.getMantle().getMantle().shouldReduce(engine)) { - return engine; - } - } - } catch (Throwable e) { - Iris.error("EngineSVC: Failed to create supplier."); - e.printStackTrace(); - Iris.reportError(e); + if (trimmer != null) { + trimmer.cancel(false); + trimmer = null; } - return null; - }; + + if (unloader != null) { + unloader.cancel(false); + unloader = null; + } + } + + @Nullable + private Engine getEngine() { + if (closed) return null; + return access.getEngine(); + } } } From 835c8422f1b0c8b05005252128c552c36266a82f Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 2 Jun 2025 17:42:36 +0200 Subject: [PATCH 043/138] calculate spawn location async --- .../engine/platform/BukkitChunkGenerator.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index 410e8bbe0..ab5d67066 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -40,6 +40,7 @@ import com.volmit.iris.util.io.ReactiveFolder; import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.Looper; +import io.papermc.lib.PaperLib; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Setter; @@ -144,6 +145,20 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun } } + @Nullable + @Override + public Location getFixedSpawnLocation(@NotNull World world, @NotNull Random random) { + Location location = new Location(world, 0, 64, 0); + PaperLib.getChunkAtAsync(location) + .thenAccept(c -> { + World w = c.getWorld(); + if (!w.getSpawnLocation().equals(location)) + return; + w.setSpawnLocation(location.add(0, w.getHighestBlockYAt(location) - 64, 0)); + }); + return location; + } + private void setupEngine() { IrisData data = IrisData.get(dataLocation); IrisDimension dimension = data.getDimensionLoader().load(dimensionKey); From ad0662be5405b74664afe9e776ecd0e0149aada8 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 2 Jun 2025 18:18:17 +0200 Subject: [PATCH 044/138] initial 1.21.5 bindings --- build.gradle | 3 +- .../java/com/volmit/iris/core/nms/INMS.java | 3 +- .../core/nms/v1_21_R4/CustomBiomeSource.java | 169 ++++ .../core/nms/v1_21_R4/IrisChunkGenerator.java | 311 +++++++ .../iris/core/nms/v1_21_R4/NMSBinding.java | 765 ++++++++++++++++++ settings.gradle | 1 + 6 files changed, 1250 insertions(+), 2 deletions(-) create mode 100644 nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/CustomBiomeSource.java create mode 100644 nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/IrisChunkGenerator.java create mode 100644 nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/NMSBinding.java diff --git a/build.gradle b/build.gradle index d9faf72b4..ec53c72ea 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ plugins { } -version '3.6.8-1.20.1-1.21.4' +version '3.6.8-1.20.1-1.21.5' // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED // ======================== WINDOWS ============================= @@ -62,6 +62,7 @@ def MAX_HEAP_SIZE = "8G" def COLOR = "truecolor" def NMS_BINDINGS = Map.of( + "v1_21_R4", "1.21.5-R0.1-SNAPSHOT", "v1_21_R3", "1.21.4-R0.1-SNAPSHOT", "v1_21_R2", "1.21.3-R0.1-SNAPSHOT", "v1_21_R1", "1.21.1-R0.1-SNAPSHOT", diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMS.java b/core/src/main/java/com/volmit/iris/core/nms/INMS.java index 3daefa900..99ef8514f 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMS.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMS.java @@ -34,7 +34,8 @@ public class INMS { "1.21.1", "v1_21_R1", "1.21.2", "v1_21_R2", "1.21.3", "v1_21_R2", - "1.21.4", "v1_21_R3" + "1.21.4", "v1_21_R3", + "1.21.5", "v1_21_R4" ); private static final List PACKS = List.of( new Version(21, 4, "31020"), diff --git a/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/CustomBiomeSource.java b/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/CustomBiomeSource.java new file mode 100644 index 000000000..570eaa0c3 --- /dev/null +++ b/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/CustomBiomeSource.java @@ -0,0 +1,169 @@ +package com.volmit.iris.core.nms.v1_21_R4; + +import com.mojang.serialization.MapCodec; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.object.IrisBiome; +import com.volmit.iris.engine.object.IrisBiomeCustom; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.math.RNG; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.Climate; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_21_R4.CraftServer; +import org.bukkit.craftbukkit.v1_21_R4.CraftWorld; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +public class CustomBiomeSource extends BiomeSource { + + private final long seed; + private final Engine engine; + private final Registry biomeCustomRegistry; + private final Registry biomeRegistry; + private final AtomicCache registryAccess = new AtomicCache<>(); + private final RNG rng; + private final KMap> customBiomes; + + public CustomBiomeSource(long seed, Engine engine, World world) { + this.engine = engine; + this.seed = seed; + this.biomeCustomRegistry = registry().lookup(Registries.BIOME).orElse(null); + this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).lookup(Registries.BIOME).orElse(null); + this.rng = new RNG(engine.getSeedManager().getBiome()); + this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine); + } + + private static List> getAllBiomes(Registry customRegistry, Registry registry, Engine engine) { + List> b = new ArrayList<>(); + + for (IrisBiome i : engine.getAllBiomes()) { + if (i.isCustom()) { + for (IrisBiomeCustom j : i.getCustomDerivitives()) { + b.add(customRegistry.get(customRegistry.getResourceKey(customRegistry + .getValue(ResourceLocation.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId()))).get()).get()); + } + } else { + b.add(NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative())); + } + } + + return b; + } + + private static Object getFor(Class type, Object source) { + Object o = fieldFor(type, source); + + if (o != null) { + return o; + } + + return invokeFor(type, source); + } + + private static Object fieldFor(Class returns, Object in) { + return fieldForClass(returns, in.getClass(), in); + } + + private static Object invokeFor(Class returns, Object in) { + for (Method i : in.getClass().getMethods()) { + if (i.getReturnType().equals(returns)) { + i.setAccessible(true); + try { + Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()"); + return i.invoke(in); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + return null; + } + + @SuppressWarnings("unchecked") + private static T fieldForClass(Class returnType, Class sourceType, Object in) { + for (Field i : sourceType.getDeclaredFields()) { + if (i.getType().equals(returnType)) { + i.setAccessible(true); + try { + Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName()); + return (T) i.get(in); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + return null; + } + + @Override + protected Stream> collectPossibleBiomes() { + return getAllBiomes( + ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())) + .lookup(Registries.BIOME).orElse(null), + ((CraftWorld) engine.getWorld().realWorld()).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null), + engine).stream(); + } + private KMap> fillCustomBiomes(Registry customRegistry, Engine engine) { + KMap> m = new KMap<>(); + + for (IrisBiome i : engine.getAllBiomes()) { + if (i.isCustom()) { + for (IrisBiomeCustom j : i.getCustomDerivitives()) { + ResourceLocation resourceLocation = ResourceLocation.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId()); + Biome biome = customRegistry.getValue(resourceLocation); + Optional> optionalBiomeKey = customRegistry.getResourceKey(biome); + if (optionalBiomeKey.isEmpty()) { + Iris.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName()); + continue; + } + ResourceKey biomeKey = optionalBiomeKey.get(); + Optional> optionalReferenceHolder = customRegistry.get(biomeKey); + if (optionalReferenceHolder.isEmpty()) { + Iris.error("Cannot find reference to biome " + biomeKey + " for engine " + engine.getName()); + continue; + } + m.put(j.getId(), optionalReferenceHolder.get()); + } + } + } + + return m; + } + + private RegistryAccess registry() { + return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())); + } + + @Override + protected MapCodec codec() { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { + int m = (y - engine.getMinHeight()) << 2; + IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2); + if (ib.isCustom()) { + return customBiomes.get(ib.getCustomBiome(rng, x << 2, m, z << 2).getId()); + } else { + org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2); + return NMSBinding.biomeToBiomeBase(biomeRegistry, v); + } + } +} \ No newline at end of file diff --git a/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/IrisChunkGenerator.java b/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/IrisChunkGenerator.java new file mode 100644 index 000000000..28513cd77 --- /dev/null +++ b/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/IrisChunkGenerator.java @@ -0,0 +1,311 @@ +package com.volmit.iris.core.nms.v1_21_R4; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.MapCodec; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.ResultLocator; +import com.volmit.iris.engine.framework.WrongEngineBroException; +import com.volmit.iris.engine.object.IrisJigsawStructure; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.math.Position2; +import com.volmit.iris.util.reflect.WrappedField; +import net.minecraft.core.*; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.StructureTags; +import net.minecraft.tags.TagKey; +import net.minecraft.util.random.WeightedList; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.*; +import net.minecraft.world.level.biome.*; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.StructureSet; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_21_R4.CraftWorld; +import org.bukkit.craftbukkit.v1_21_R4.generator.CustomChunkGenerator; +import org.spigotmc.SpigotWorldConfig; + +import javax.annotation.Nullable; +import java.lang.reflect.Field; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class IrisChunkGenerator extends CustomChunkGenerator { + private static final WrappedField BIOME_SOURCE; + private final ChunkGenerator delegate; + private final Engine engine; + private final KMap, KSet> structures = new KMap<>(); + + public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { + super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null); + this.delegate = delegate; + this.engine = engine; + var dimension = engine.getDimension(); + + KSet placements = new KSet<>(); + addAll(dimension.getJigsawStructures(), placements); + for (var region : dimension.getAllRegions(engine)) { + addAll(region.getJigsawStructures(), placements); + for (var biome : region.getAllBiomes(engine)) + addAll(biome.getJigsawStructures(), placements); + } + var stronghold = dimension.getStronghold(); + if (stronghold != null) + placements.add(engine.getData().getJigsawStructureLoader().load(stronghold)); + placements.removeIf(Objects::isNull); + + var registry = ((CraftWorld) world).getHandle().registryAccess().lookup(Registries.STRUCTURE).orElseThrow(); + for (var s : placements) { + try { + String raw = s.getStructureKey(); + if (raw == null) continue; + boolean tag = raw.startsWith("#"); + if (tag) raw = raw.substring(1); + + var location = ResourceLocation.parse(raw); + if (!tag) { + structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey()); + continue; + } + + var key = TagKey.create(Registries.STRUCTURE, location); + var set = registry.get(key).orElse(null); + if (set == null) { + Iris.error("Could not find structure tag: " + raw); + continue; + } + for (var holder : set) { + var resourceKey = holder.unwrapKey().orElse(null); + if (resourceKey == null) continue; + structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey()); + } + } catch (Throwable e) { + Iris.error("Failed to load structure: " + s.getLoadKey()); + e.printStackTrace(); + } + } + } + + private void addAll(KList placements, KSet structures) { + if (placements == null) return; + placements.stream() + .map(IrisJigsawStructurePlacement::getStructure) + .map(engine.getData().getJigsawStructureLoader()::load) + .filter(Objects::nonNull) + .forEach(structures::add); + } + + @Override + public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (holders.size() == 0) return null; + if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) { + var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ())); + return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0)); + } + if (engine.getDimension().isDisableExplorerMaps()) + return null; + + KMap> structures = new KMap<>(); + for (var holder : holders) { + if (holder == null) continue; + var key = holder.unwrapKey().orElse(null); + var set = this.structures.get(key); + if (set == null) continue; + for (var structure : set) { + structures.put(structure, holder); + } + } + if (structures.isEmpty()) + return null; + + var locator = ResultLocator.locateStructure(structures.keySet()) + .then((e, p , s) -> structures.get(s.getLoadKey())); + if (findUnexplored) + locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s); + + try { + var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get(); + if (result == null) return null; + var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ()); + return Pair.of(blockPos, result.obj()); + } catch (WrongEngineBroException | ExecutionException | InterruptedException e) { + return null; + } + } + + @Override + protected MapCodec codec() { + return MapCodec.unit(null); + } + + @Override + public ChunkGenerator getDelegate() { + if (delegate instanceof CustomChunkGenerator chunkGenerator) + return chunkGenerator.getDelegate(); + return delegate; + } + + @Override + public int getMinY() { + return delegate.getMinY(); + } + + @Override + public int getSeaLevel() { + return delegate.getSeaLevel(); + } + + @Override + public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey resourcekey) { + delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey); + } + + @Override + public ChunkGeneratorStructureState createState(HolderLookup holderlookup, RandomState randomstate, long i, SpigotWorldConfig conf) { + return delegate.createState(holderlookup, randomstate, i, conf); + } + + @Override + public void createReferences(WorldGenLevel generatoraccessseed, StructureManager structuremanager, ChunkAccess ichunkaccess) { + delegate.createReferences(generatoraccessseed, structuremanager, ichunkaccess); + } + + @Override + public CompletableFuture createBiomes(RandomState randomstate, Blender blender, StructureManager structuremanager, ChunkAccess ichunkaccess) { + return delegate.createBiomes(randomstate, blender, structuremanager, ichunkaccess); + } + + @Override + public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) { + delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess); + } + + @Override + public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess) { + delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess); + } + + @Override + public CompletableFuture fillFromNoise(Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) { + return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess); + } + + @Override + public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public WeightedList getMobsAt(Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { + return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition); + } + + @Override + public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager); + } + + @Override + public void addDebugScreenInfo(List list, RandomState randomstate, BlockPos blockposition) { + delegate.addDebugScreenInfo(list, randomstate, blockposition); + } + + @Override + public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) { + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, vanilla); + } + + @Override + public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public void addVanillaDecorations(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { + delegate.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager); + } + + @Override + public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) { + delegate.spawnOriginalMobs(regionlimitedworldaccess); + } + + @Override + public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) { + return delegate.getSpawnHeight(levelheightaccessor); + } + + @Override + public int getGenDepth() { + return delegate.getGenDepth(); + } + + @Override + public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate); + } + + @Override + public Optional>> getTypeNameForDataFixer() { + return delegate.getTypeNameForDataFixer(); + } + + @Override + public void validate() { + delegate.validate(); + } + + @Override + @SuppressWarnings("deprecation") + public BiomeGenerationSettings getBiomeGenerationSettings(Holder holder) { + return delegate.getBiomeGenerationSettings(holder); + } + + static { + Field biomeSource = null; + for (Field field : ChunkGenerator.class.getDeclaredFields()) { + if (!field.getType().equals(BiomeSource.class)) + continue; + biomeSource = field; + break; + } + if (biomeSource == null) + throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!"); + BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName()); + } + + private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) { + try { + BIOME_SOURCE.set(generator, source); + if (generator instanceof CustomChunkGenerator custom) + BIOME_SOURCE.set(custom.getDelegate(), source); + + return generator; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } +} diff --git a/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/NMSBinding.java b/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/NMSBinding.java new file mode 100644 index 000000000..3b11e017b --- /dev/null +++ b/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/NMSBinding.java @@ -0,0 +1,765 @@ +package com.volmit.iris.core.nms.v1_21_R4; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.serialization.Lifecycle; +import com.volmit.iris.Iris; +import com.volmit.iris.core.nms.INMSBinding; +import com.volmit.iris.core.nms.container.AutoClosing; +import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.hunk.Hunk; +import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; +import com.volmit.iris.util.matter.MatterBiomeInject; +import com.volmit.iris.util.nbt.mca.NBTWorld; +import com.volmit.iris.util.nbt.mca.palette.*; +import com.volmit.iris.util.nbt.tag.CompoundTag; +import com.volmit.iris.util.scheduling.J; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import lombok.SneakyThrows; +import net.minecraft.core.Registry; +import net.minecraft.core.*; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.Tag; +import net.minecraft.nbt.*; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.WorldLoader; +import net.minecraft.server.commands.data.BlockDataAccessor; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import net.minecraft.world.level.chunk.status.WorldGenContext; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.levelgen.FlatLevelSource; +import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; +import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import org.bukkit.*; +import org.bukkit.block.Biome; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.v1_21_R4.CraftChunk; +import org.bukkit.craftbukkit.v1_21_R4.CraftServer; +import org.bukkit.craftbukkit.v1_21_R4.CraftWorld; +import org.bukkit.craftbukkit.v1_21_R4.block.CraftBlockState; +import org.bukkit.craftbukkit.v1_21_R4.block.CraftBlockStates; +import org.bukkit.craftbukkit.v1_21_R4.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R4.util.CraftNamespacedKey; +import org.bukkit.entity.Entity; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.awt.Color; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; + +public class NMSBinding implements INMSBinding { + private final KMap baseBiomeCache = new KMap<>(); + private final BlockData AIR = Material.AIR.createBlockData(); + private final AtomicCache> biomeMapCache = new AtomicCache<>(); + private final AtomicCache dataLoadContext = new AtomicCache<>(); + private final AtomicCache> registryCache = new AtomicCache<>(); + private final AtomicCache> globalCache = new AtomicCache<>(); + private final AtomicCache registryAccess = new AtomicCache<>(); + private final ReentrantLock dataContextLock = new ReentrantLock(true); + private final AtomicCache byIdRef = new AtomicCache<>(); + private Field biomeStorageCache = null; + + private static Object getFor(Class type, Object source) { + Object o = fieldFor(type, source); + + if (o != null) { + return o; + } + + return invokeFor(type, source); + } + + private static Object invokeFor(Class returns, Object in) { + for (Method i : in.getClass().getMethods()) { + if (i.getReturnType().equals(returns)) { + i.setAccessible(true); + try { + Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()"); + return i.invoke(in); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + return null; + } + + private static Object fieldFor(Class returns, Object in) { + return fieldForClass(returns, in.getClass(), in); + } + + @SuppressWarnings("unchecked") + private static T fieldForClass(Class returnType, Class sourceType, Object in) { + for (Field i : sourceType.getDeclaredFields()) { + if (i.getType().equals(returnType)) { + i.setAccessible(true); + try { + Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName()); + return (T) i.get(in); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + return null; + } + + private static Class getClassType(Class type, int ordinal) { + return type.getDeclaredClasses()[ordinal]; + } + + @Override + public boolean hasTile(Material material) { + return !CraftBlockState.class.equals(CraftBlockStates.getBlockStateType(material)); + } + + @Override + public boolean hasTile(Location l) { + return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ()), false) != null; + } + + @Override + @SuppressWarnings("unchecked") + public KMap serializeTile(Location location) { + BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), false); + + if (e == null) { + return null; + } + + net.minecraft.nbt.CompoundTag tag = e.saveWithoutMetadata(registry()); + return (KMap) convertFromTag(tag, 0, 64); + } + + @Contract(value = "null, _, _ -> null", pure = true) + private Object convertFromTag(net.minecraft.nbt.Tag tag, int depth, int maxDepth) { + if (tag == null || depth > maxDepth) return null; + return switch (tag) { + case CollectionTag collection -> { + KList list = new KList<>(); + + for (Object i : collection) { + if (i instanceof net.minecraft.nbt.Tag t) + list.add(convertFromTag(t, depth + 1, maxDepth)); + else list.add(i); + } + yield list; + } + case net.minecraft.nbt.CompoundTag compound -> { + KMap map = new KMap<>(); + + for (String key : compound.keySet()) { + var child = compound.get(key); + if (child == null) continue; + var value = convertFromTag(child, depth + 1, maxDepth); + if (value == null) continue; + map.put(key, value); + } + yield map; + } + case NumericTag numeric -> numeric.box(); + default -> tag.asString().orElse(null); + }; + } + + @Override + public void deserializeTile(KMap map, Location pos) { + net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) convertToTag(map, 0, 64); + var level = ((CraftWorld) pos.getWorld()).getHandle(); + var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()); + J.s(() -> merge(level, blockPos, tag)); + } + + private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) { + var blockEntity = level.getBlockEntity(blockPos); + if (blockEntity == null) { + Iris.warn("[NMS] BlockEntity not found at " + blockPos); + var state = level.getBlockState(blockPos); + if (!state.hasBlockEntity()) + return; + + blockEntity = ((EntityBlock) state.getBlock()) + .newBlockEntity(blockPos, state); + } + var accessor = new BlockDataAccessor(blockEntity, blockPos); + accessor.setData(accessor.getData().merge(tag)); + } + + private Tag convertToTag(Object object, int depth, int maxDepth) { + if (object == null || depth > maxDepth) return EndTag.INSTANCE; + return switch (object) { + case Map map -> { + var tag = new net.minecraft.nbt.CompoundTag(); + for (var i : map.entrySet()) { + tag.put(i.getKey().toString(), convertToTag(i.getValue(), depth + 1, maxDepth)); + } + yield tag; + } + case List list -> { + var tag = new net.minecraft.nbt.ListTag(); + for (var i : list) { + tag.add(convertToTag(i, depth + 1, maxDepth)); + } + yield tag; + } + case Byte number -> ByteTag.valueOf(number); + case Short number -> ShortTag.valueOf(number); + case Integer number -> IntTag.valueOf(number); + case Long number -> LongTag.valueOf(number); + case Float number -> FloatTag.valueOf(number); + case Double number -> DoubleTag.valueOf(number); + case String string -> StringTag.valueOf(string); + default -> EndTag.INSTANCE; + }; + } + + @Override + public CompoundTag serializeEntity(Entity location) { + return null;// TODO: + } + + @Override + public Entity deserializeEntity(CompoundTag s, Location newPosition) { + return null;// TODO: + } + + @Override + public boolean supportsCustomHeight() { + return true; + } + + private RegistryAccess registry() { + return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())); + } + + private Registry getCustomBiomeRegistry() { + return registry().lookup(Registries.BIOME).orElse(null); + } + + private Registry getBlockRegistry() { + return registry().lookup(Registries.BLOCK).orElse(null); + } + + @Override + public Object getBiomeBaseFromId(int id) { + return getCustomBiomeRegistry().get(id); + } + + @Override + public int getMinHeight(World world) { + return world.getMinHeight(); + } + + @Override + public boolean supportsCustomBiomes() { + return true; + } + + @Override + public int getTrueBiomeBaseId(Object biomeBase) { + return getCustomBiomeRegistry().getId(((Holder) biomeBase).value()); + } + + @Override + public Object getTrueBiomeBase(Location location) { + return ((CraftWorld) location.getWorld()).getHandle().getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ())); + } + + @Override + public String getTrueBiomeBaseKey(Location location) { + return getKeyForBiomeBase(getTrueBiomeBase(location)); + } + + @Override + public Object getCustomBiomeBaseFor(String mckey) { + return getCustomBiomeRegistry().getValue(ResourceLocation.parse(mckey)); + } + + @Override + public Object getCustomBiomeBaseHolderFor(String mckey) { + return getCustomBiomeRegistry().get(getTrueBiomeBaseId(getCustomBiomeRegistry().get(ResourceLocation.parse(mckey)))).orElse(null); + } + + public int getBiomeBaseIdForKey(String key) { + return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(ResourceLocation.parse(key)).map(Holder::value).orElse(null)); + } + + @Override + public String getKeyForBiomeBase(Object biomeBase) { + return getCustomBiomeRegistry().getKey((net.minecraft.world.level.biome.Biome) biomeBase).getPath(); // something, not something:something + } + + @Override + public Object getBiomeBase(World world, Biome biome) { + return biomeToBiomeBase(((CraftWorld) world).getHandle() + .registryAccess().lookup(Registries.BIOME).orElse(null), biome); + } + + @Override + public Object getBiomeBase(Object registry, Biome biome) { + Object v = baseBiomeCache.get(biome); + + if (v != null) { + return v; + } + //noinspection unchecked + v = biomeToBiomeBase((Registry) registry, biome); + if (v == null) { + // Ok so there is this new biome name called "CUSTOM" in Paper's new releases. + // But, this does NOT exist within CraftBukkit which makes it return an error. + // So, we will just return the ID that the plains biome returns instead. + //noinspection unchecked + return biomeToBiomeBase((Registry) registry, Biome.PLAINS); + } + baseBiomeCache.put(biome, v); + return v; + } + + @Override + public KList getBiomes() { + return new KList<>(Biome.values()).qadd(Biome.CHERRY_GROVE).qdel(Biome.CUSTOM); + } + + @Override + public boolean isBukkit() { + return true; + } + + @Override + public int getBiomeId(Biome biome) { + for (World i : Bukkit.getWorlds()) { + if (i.getEnvironment().equals(World.Environment.NORMAL)) { + Registry registry = ((CraftWorld) i).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null); + return registry.getId((net.minecraft.world.level.biome.Biome) getBiomeBase(registry, biome)); + } + } + + return biome.ordinal(); + } + + private MCAIdMap getBiomeMapping() { + return biomeMapCache.aquire(() -> new MCAIdMap<>() { + @NotNull + @Override + public Iterator iterator() { + return getCustomBiomeRegistry().iterator(); + } + + @Override + public int getId(net.minecraft.world.level.biome.Biome paramT) { + return getCustomBiomeRegistry().getId(paramT); + } + + @Override + public net.minecraft.world.level.biome.Biome byId(int paramInt) { + return (net.minecraft.world.level.biome.Biome) getBiomeBaseFromId(paramInt); + } + }); + } + + @NotNull + private MCABiomeContainer getBiomeContainerInterface(MCAIdMap biomeMapping, MCAChunkBiomeContainer base) { + return new MCABiomeContainer() { + @Override + public int[] getData() { + return base.writeBiomes(); + } + + @Override + public void setBiome(int x, int y, int z, int id) { + base.setBiome(x, y, z, biomeMapping.byId(id)); + } + + @Override + public int getBiome(int x, int y, int z) { + return biomeMapping.getId(base.getBiome(x, y, z)); + } + }; + } + + @Override + public MCABiomeContainer newBiomeContainer(int min, int max) { + MCAChunkBiomeContainer base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max); + return getBiomeContainerInterface(getBiomeMapping(), base); + } + + @Override + public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) { + MCAChunkBiomeContainer base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max, data); + return getBiomeContainerInterface(getBiomeMapping(), base); + } + + @Override + public int countCustomBiomes() { + AtomicInteger a = new AtomicInteger(0); + + getCustomBiomeRegistry().keySet().forEach((i) -> { + if (i.getNamespace().equals("minecraft")) { + return; + } + + a.incrementAndGet(); + Iris.debug("Custom Biome: " + i); + }); + + return a.get(); + } + + public boolean supportsDataPacks() { + return true; + } + + public void setBiomes(int cx, int cz, World world, Hunk biomes) { + LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz); + biomes.iterateSync((x, y, z, b) -> c.setBiome(x, y, z, (Holder) b)); + c.markUnsaved(); + } + + @Override + public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) { + try { + ChunkAccess s = (ChunkAccess) getFieldForBiomeStorage(chunk).get(chunk); + Holder biome = (Holder) somethingVeryDirty; + s.setBiome(x, y, z, biome); + } catch (IllegalAccessException e) { + Iris.reportError(e); + e.printStackTrace(); + } + } + + private Field getFieldForBiomeStorage(Object storage) { + Field f = biomeStorageCache; + + if (f != null) { + return f; + } + try { + f = storage.getClass().getDeclaredField("biome"); + f.setAccessible(true); + return f; + } catch (Throwable e) { + Iris.reportError(e); + e.printStackTrace(); + Iris.error(storage.getClass().getCanonicalName()); + } + + biomeStorageCache = f; + return null; + } + + @Override + public MCAPaletteAccess createPalette() { + MCAIdMapper registry = registryCache.aquireNasty(() -> { + Field cf = net.minecraft.core.IdMapper.class.getDeclaredField("tToId"); + Field df = net.minecraft.core.IdMapper.class.getDeclaredField("idToT"); + Field bf = net.minecraft.core.IdMapper.class.getDeclaredField("nextId"); + cf.setAccessible(true); + df.setAccessible(true); + bf.setAccessible(true); + net.minecraft.core.IdMapper blockData = Block.BLOCK_STATE_REGISTRY; + int b = bf.getInt(blockData); + Object2IntMap c = (Object2IntMap) cf.get(blockData); + List d = (List) df.get(blockData); + return new MCAIdMapper(c, d, b); + }); + MCAPalette global = globalCache.aquireNasty(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState())); + MCAPalettedContainer container = new MCAPalettedContainer<>(global, registry, + i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState(), + i -> NBTWorld.getCompound(CraftBlockData.fromData(i)), + ((CraftBlockData) AIR).getState()); + return new MCAWrappedPalettedContainer<>(container, + i -> NBTWorld.getCompound(CraftBlockData.fromData(i)), + i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState()); + } + + @Override + public void injectBiomesFromMantle(Chunk e, Mantle mantle) { + ChunkAccess chunk = ((CraftChunk) e).getHandle(ChunkStatus.FULL); + AtomicInteger c = new AtomicInteger(); + AtomicInteger r = new AtomicInteger(); + mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x, y, z, b) -> { + if (b != null) { + if (b.isCustom()) { + chunk.setBiome(x, y, z, getCustomBiomeRegistry().get(b.getBiomeId()).get()); + c.getAndIncrement(); + } else { + chunk.setBiome(x, y, z, (Holder) getBiomeBase(e.getWorld(), b.getBiome())); + r.getAndIncrement(); + } + } + }); + } + + public ItemStack applyCustomNbt(ItemStack itemStack, KMap customNbt) throws IllegalArgumentException { + if (customNbt != null && !customNbt.isEmpty()) { + net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack); + + try { + net.minecraft.nbt.CompoundTag tag = TagParser.parseCompoundFully((new JSONObject(customNbt)).toString()); + tag.merge(s.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).getUnsafe()); + s.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + } catch (CommandSyntaxException var5) { + throw new IllegalArgumentException(var5); + } + + return CraftItemStack.asBukkitCopy(s); + } else { + return itemStack; + } + } + + public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { + var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap; + var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class); + worldGenContextField.setAccessible(true); + var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap); + var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null); + if (dimensionType != null && !dimensionType.location().getNamespace().equals("iris")) + Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().toString()); + + var newContext = new WorldGenContext( + worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world), + worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadExecutor(), worldGenContext.unsavedListener()); + + worldGenContextField.set(chunkMap, newContext); + } + + public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { + Field[] fields = EntityType.class.getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) { + try { + EntityType entityType = (EntityType) field.get(null); + if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) { + Vector v1 = new Vector<>(); + v1.add(entityType.getHeight()); + entityType.getDimensions(); + Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth()); + //System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width); + return box; + } + } catch (IllegalAccessException e) { + Iris.error("Unable to get entity dimensions!"); + e.printStackTrace(); + } + } + } + return null; + } + + + @Override + public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) { + return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason); + } + + @Override + public java.awt.Color getBiomeColor(Location location, BiomeColor type) { + LevelReader reader = ((CraftWorld) location.getWorld()).getHandle(); + var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ())); + var biome = holder.value(); + if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null)); + + int rgba = switch (type) { + case FOG -> biome.getFogColor(); + case WATER -> biome.getWaterColor(); + case WATER_FOG -> biome.getWaterFogColor(); + case SKY -> biome.getSkyColor(); + case FOLIAGE -> biome.getFoliageColor(); + case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ()); + }; + if (rgba == 0) { + if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().getFoliageColorOverride().isEmpty()) + return null; + if (BiomeColor.GRASS == type && biome.getSpecialEffects().getGrassColorOverride().isEmpty()) + return null; + } + return new Color(rgba, true); + } + + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { + try { + for (Field f : clazz.getDeclaredFields()) { + if (f.getType().equals(fieldType)) + return f; + } + throw new NoSuchFieldException(fieldType.getName()); + } catch (NoSuchFieldException var4) { + Class superClass = clazz.getSuperclass(); + if (superClass == null) { + throw var4; + } else { + return getField(superClass, fieldType); + } + } + } + + public static Holder biomeToBiomeBase(Registry registry, Biome biome) { + return registry.getOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey()))); + } + + @Override + public DataVersion getDataVersion() { + return DataVersion.V1213; + } + + @Override + public int getSpawnChunkCount(World world) { + var radius = Optional.ofNullable(world.getGameRuleValue(GameRule.SPAWN_CHUNK_RADIUS)) + .orElseGet(() -> world.getGameRuleDefault(GameRule.SPAWN_CHUNK_RADIUS)); + if (radius == null) throw new IllegalStateException("GameRule.SPAWN_CHUNK_RADIUS is null!"); + return (int) Math.pow(2 * radius + 1, 2); + } + + @Override + public KList getStructureKeys() { + KList keys = new KList<>(); + + var registry = registry().lookup(Registries.STRUCTURE).orElse(null); + if (registry == null) return keys; + registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add); + registry.getTags() + .map(HolderSet.Named::key) + .map(TagKey::location) + .map(ResourceLocation::toString) + .map(s -> "#" + s) + .forEach(keys::add); + + return keys; + } + + @Override + @SneakyThrows + public AutoClosing injectLevelStems() { + if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); + + var server = ((CraftServer) Bukkit.getServer()); + var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); + var nmsServer = server.getServer(); + var old = nmsServer.worldLoader; + + field.setAccessible(true); + field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( + old.resources(), + old.dataConfiguration(), + old.datapackWorldgen(), + createRegistryAccess(old.datapackDimensions(), false, true, true, true) + ))); + + return new AutoClosing(() -> { + field.set(nmsServer, old); + dataContextLock.unlock(); + }); + } + + @Override + @SneakyThrows + public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { + var reg = registry(); + var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); + field.setAccessible(true); + + var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); + var injected = access.lookupOrThrow(Registries.LEVEL_STEM); + var old = (Map>, Registry>) field.get(reg); + var fake = new HashMap<>(old); + fake.put(Registries.LEVEL_STEM, injected); + field.set(reg, fake); + + return new AutoClosing(() -> field.set(reg, old)); + } + + @Override + public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { + var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE); + if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD)); + if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); + if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); + return overworld || nether || end; + } + + @Override + public void removeCustomDimensions(World world) { + ((CraftWorld) world).getHandle().L.customDimensions = null; + } + + private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { + var access = registry(); + var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE); + + var settings = new FlatLevelGeneratorSettings( + Optional.empty(), + access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), + List.of() + ); + settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); + settings.updateLayers(); + + var source = new FlatLevelSource(settings); + var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental()); + if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD); + if (nether) register(fake, dimensions, source, LevelStem.NETHER); + if (end) register(fake, dimensions, source, LevelStem.END); + copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null)); + + if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM)); + + return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); + } + + private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { + var loc = createIrisKey(key); + target.register(key, new LevelStem( + dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), + source + ), RegistrationInfo.BUILT_IN); + } + + private void copy(MappedRegistry target, Registry source) { + if (source == null) return; + source.listElementIds().forEach(key -> { + var value = source.getValue(key); + var info = source.registrationInfo(key).orElse(null); + if (value != null && info != null && !target.containsKey(key)) + target.register(key, value, info); + }); + } + + private ResourceLocation createIrisKey(ResourceKey key) { + return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); + } +} diff --git a/settings.gradle b/settings.gradle index 57ad35b97..9315a1131 100644 --- a/settings.gradle +++ b/settings.gradle @@ -30,6 +30,7 @@ rootProject.name = 'Iris' include(':core') include( + ':nms:v1_21_R4', ':nms:v1_21_R3', ':nms:v1_21_R2', ':nms:v1_21_R1', From 635ee024599952a6fa15615977c4e625e761e2cb Mon Sep 17 00:00:00 2001 From: Aidan Aeternum Date: Mon, 2 Jun 2025 18:48:01 -0400 Subject: [PATCH 045/138] v+ --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ec53c72ea..88f7a3b94 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ plugins { } -version '3.6.8-1.20.1-1.21.5' +version '3.6.9-1.20.1-1.21.5' // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED // ======================== WINDOWS ============================= From 48cc6bb49c18b81b350d2d25fd95461cc7e073e2 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 4 Jun 2025 15:03:00 +0200 Subject: [PATCH 046/138] improve mantle writer speeds --- .../iris/engine/mantle/MantleWriter.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java b/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java index 4c32af6d8..b71051472 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java @@ -60,8 +60,9 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { this.x = x; this.z = z; - for (int i = -radius; i <= radius; i++) { - for (int j = -radius; j <= radius; j++) { + int r = radius / 4; + for (int i = -r; i <= r; i++) { + for (int j = -r; j <= r; j++) { cachedChunks.put(Cache.key(i + x, j + z), mantle.getChunk(i + x, j + z).use()); } } @@ -143,7 +144,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { if (cx >= this.x - radius && cx <= this.x + radius && cz >= this.z - radius && cz <= this.z + radius) { - MantleChunk chunk = cachedChunks.get(Cache.key(cx, cz)); + MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use()); if (chunk == null) { Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)"); @@ -152,6 +153,8 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { Matter matter = chunk.getOrCreate(y >> 4); matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t); + } else { + Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz); } } @@ -639,9 +642,10 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { @Override public void close() { - cachedChunks.values().removeIf(c -> { - c.release(); - return true; - }); + var iterator = cachedChunks.values().iterator(); + while (iterator.hasNext()) { + iterator.next().release(); + iterator.remove(); + } } } From a56cd4c268e68a0000cbe9ef92c2bd2f2d2e2c8b Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 4 Jun 2025 16:32:29 +0200 Subject: [PATCH 047/138] use block data use for slice --- .../java/com/volmit/iris/util/matter/slices/BlockMatter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/util/matter/slices/BlockMatter.java b/core/src/main/java/com/volmit/iris/util/matter/slices/BlockMatter.java index 5322ca5de..15afb4376 100644 --- a/core/src/main/java/com/volmit/iris/util/matter/slices/BlockMatter.java +++ b/core/src/main/java/com/volmit/iris/util/matter/slices/BlockMatter.java @@ -18,10 +18,10 @@ package com.volmit.iris.util.matter.slices; +import com.volmit.iris.util.data.B; import com.volmit.iris.util.data.IrisCustomData; import com.volmit.iris.util.data.palette.Palette; import com.volmit.iris.util.matter.Sliced; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.data.BlockData; @@ -63,6 +63,6 @@ public class BlockMatter extends RawMatter { @Override public BlockData readNode(DataInputStream din) throws IOException { - return Bukkit.createBlockData(din.readUTF()); + return B.get(din.readUTF()); } } From 5705caa1babbbfe03fa459a4ef710b3782053c88 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 4 Jun 2025 20:20:48 +0200 Subject: [PATCH 048/138] fix benchmarking not disabling properly --- .../core/pregenerator/IrisPregenerator.java | 8 +++-- .../iris/core/tools/IrisPackBenchmarking.java | 34 +++++++++---------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java b/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java index 3201bebf4..dbd734a56 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java @@ -66,8 +66,10 @@ public class IrisPregenerator { private final KSet net; private final ChronoLatch cl; private final ChronoLatch saveLatch = new ChronoLatch(30000); + private final IrisPackBenchmarking benchmarking; public IrisPregenerator(PregenTask task, PregeneratorMethod generator, PregenListener listener) { + benchmarking = IrisPackBenchmarking.getInstance(); this.listener = listenify(listener); cl = new ChronoLatch(5000); generatedRegions = new KSet<>(); @@ -135,7 +137,7 @@ public class IrisPregenerator { double percentage = ((double) generated.get() / (double) totalChunks.get()) * 100; Iris.info("%s: %s of %s (%.0f%%), %s/s ETA: %s", - IrisPackBenchmarking.benchmarkInProgress ? "Benchmarking" : "Pregen", + benchmarking != null ? "Benchmarking" : "Pregen", Form.f(generated.get()), Form.f(totalChunks.get()), percentage, @@ -174,10 +176,10 @@ public class IrisPregenerator { task.iterateRegions((x, z) -> visitRegion(x, z, false)); Iris.info("Pregen took " + Form.duration((long) p.getMilliseconds())); shutdown(); - if (!IrisPackBenchmarking.benchmarkInProgress) { + if (benchmarking == null) { Iris.info(C.IRIS + "Pregen stopped."); } else { - IrisPackBenchmarking.instance.finishedBenchmark(chunksPerSecondHistory); + benchmarking.finishedBenchmark(chunksPerSecondHistory); } } diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisPackBenchmarking.java b/core/src/main/java/com/volmit/iris/core/tools/IrisPackBenchmarking.java index d9c6b0bd0..4f39a7527 100644 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisPackBenchmarking.java +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisPackBenchmarking.java @@ -12,7 +12,6 @@ import com.volmit.iris.util.format.Form; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.PrecisionStopwatch; -import lombok.Getter; import org.bukkit.Bukkit; import java.io.File; @@ -24,28 +23,28 @@ import java.util.Collections; public class IrisPackBenchmarking { - @Getter - public static IrisPackBenchmarking instance; - public static boolean benchmarkInProgress = false; + private static final ThreadLocal instance = new ThreadLocal<>(); private final PrecisionStopwatch stopwatch = new PrecisionStopwatch(); private final IrisDimension dimension; private final int radius; private final boolean gui; public IrisPackBenchmarking(IrisDimension dimension, int radius, boolean gui) { - instance = this; this.dimension = dimension; this.radius = radius; this.gui = gui; runBenchmark(); } + public static IrisPackBenchmarking getInstance() { + return instance.get(); + } + private void runBenchmark() { Thread.ofVirtual() .name("PackBenchmarking") .start(() -> { Iris.info("Setting up benchmark environment "); - benchmarkInProgress = true; IO.delete(new File(Bukkit.getWorldContainer(), "benchmark")); createBenchmark(); while (!IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) { @@ -59,10 +58,6 @@ public class IrisPackBenchmarking { } - public boolean getBenchmarkInProgress() { - return benchmarkInProgress; - } - public void finishedBenchmark(KList cps) { try { String time = Form.duration((long) stopwatch.getMilliseconds()); @@ -132,13 +127,18 @@ public class IrisPackBenchmarking { } private void startBenchmark() { - IrisToolbelt.pregenerate(PregenTask - .builder() - .gui(gui) - .radiusX(radius) - .radiusZ(radius) - .build(), Bukkit.getWorld("benchmark") - ); + try { + instance.set(this); + IrisToolbelt.pregenerate(PregenTask + .builder() + .gui(gui) + .radiusX(radius) + .radiusZ(radius) + .build(), Bukkit.getWorld("benchmark") + ); + } finally { + instance.remove(); + } } private double calculateAverage(KList list) { From abb1d9cd627569f16dcffb8ab1eaeec5c8665118 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Thu, 5 Jun 2025 20:26:16 +0200 Subject: [PATCH 049/138] add bytebuddy binding for 1.21.5 --- .../iris/core/nms/v1_21_R4/NMSBinding.java | 175 ++++++++---------- 1 file changed, 80 insertions(+), 95 deletions(-) diff --git a/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/NMSBinding.java b/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/NMSBinding.java index 3b11e017b..6766d2ab8 100644 --- a/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/NMSBinding.java +++ b/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/NMSBinding.java @@ -1,16 +1,17 @@ package com.volmit.iris.core.nms.v1_21_R4; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.serialization.Lifecycle; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; -import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.platform.PlatformChunkGenerator; +import com.volmit.iris.util.agent.Agent; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.format.C; import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; @@ -21,20 +22,24 @@ import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; import it.unimi.dsi.fastutil.objects.Object2IntMap; -import lombok.SneakyThrows; -import net.minecraft.core.Registry; +import it.unimi.dsi.fastutil.shorts.ShortList; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.*; +import net.minecraft.core.Registry; import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.Tag; import net.minecraft.nbt.*; +import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.WorldLoader; import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.tags.TagKey; +import net.minecraft.world.RandomSequences; import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.level.LevelReader; @@ -46,13 +51,16 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.WorldGenContext; -import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.chunk.storage.SerializableChunkData; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -66,6 +74,7 @@ import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_21_R4.util.CraftNamespacedKey; import org.bukkit.entity.Entity; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; @@ -73,22 +82,23 @@ import org.jetbrains.annotations.NotNull; import java.awt.Color; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.List; import java.util.*; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); - private final AtomicCache dataLoadContext = new AtomicCache<>(); + private final AtomicBoolean injected = new AtomicBoolean(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); - private final ReentrantLock dataContextLock = new ReentrantLock(true); private final AtomicCache byIdRef = new AtomicCache<>(); private Field biomeStorageCache = null; @@ -663,103 +673,78 @@ public class NMSBinding implements INMSBinding { } @Override - @SneakyThrows - public AutoClosing injectLevelStems() { - if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!"); - - var server = ((CraftServer) Bukkit.getServer()); - var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class); - var nmsServer = server.getServer(); - var old = nmsServer.worldLoader; - - field.setAccessible(true); - field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( - old.resources(), - old.dataConfiguration(), - old.datapackWorldgen(), - createRegistryAccess(old.datapackDimensions(), false, true, true, true) - ))); - - return new AutoClosing(() -> { - field.set(nmsServer, old); - dataContextLock.unlock(); - }); + public boolean missingDimensionTypes(String... keys) { + var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE); + return !Arrays.stream(keys) + .map(key -> ResourceLocation.fromNamespaceAndPath("iris", key)) + .allMatch(type::containsKey); } @Override - @SneakyThrows - public AutoClosing injectUncached(boolean overworld, boolean nether, boolean end) { - var reg = registry(); - var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class); - field.setAccessible(true); + public boolean injectBukkit() { + if (injected.getAndSet(true)) + return true; + try { + Iris.info("Injecting Bukkit"); + var buddy = new ByteBuddy(); + buddy.redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( + MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, + ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, + boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), Agent.installed()); - var access = createRegistryAccess(((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions(), true, overworld, nether, end); - var injected = access.lookupOrThrow(Registries.LEVEL_STEM); - var old = (Map>, Registry>) field.get(reg); - var fake = new HashMap<>(old); - fake.put(Registries.LEVEL_STEM, injected); - field.set(reg, fake); - - return new AutoClosing(() -> field.set(reg, old)); + return true; + } catch (Throwable e) { + Iris.error(C.RED + "Failed to inject Bukkit"); + e.printStackTrace(); + } + return false; } - @Override - public boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end) { - var registry = registry().lookupOrThrow(Registries.DIMENSION_TYPE); - if (overworld) overworld = !registry.containsKey(createIrisKey(LevelStem.OVERWORLD)); - if (nether) nether = !registry.containsKey(createIrisKey(LevelStem.NETHER)); - if (end) end = !registry.containsKey(createIrisKey(LevelStem.END)); - return overworld || nether || end; + public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { + if (!(raw instanceof PlatformChunkGenerator gen)) + throw new IllegalStateException("Generator is not platform chunk generator!"); + + var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey()); + var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey)); + return new LevelStem(dimensionType, chunkGenerator(access)); } - @Override - public void removeCustomDimensions(World world) { - ((CraftWorld) world).getHandle().L.customDimensions = null; - } - - private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) { - var access = registry(); - var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE); - - var settings = new FlatLevelGeneratorSettings( - Optional.empty(), - access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), - List.of() - ); + private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { + var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of()); settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); settings.updateLayers(); - - var source = new FlatLevelSource(settings); - var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental()); - if (overworld) register(fake, dimensions, source, LevelStem.OVERWORLD); - if (nether) register(fake, dimensions, source, LevelStem.NETHER); - if (end) register(fake, dimensions, source, LevelStem.END); - copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null)); - - if (copy) copy(fake, access.lookupOrThrow(Registries.LEVEL_STEM)); - - return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake)).freeze(); + return new FlatLevelSource(settings); } - private void register(MappedRegistry target, Registry dimensions, FlatLevelSource source, ResourceKey key) { - var loc = createIrisKey(key); - target.register(key, new LevelStem( - dimensions.get(loc).orElseThrow(() -> new IllegalStateException("Missing dimension type " + loc + " in " + dimensions.keySet())), - source - ), RegistrationInfo.BUILT_IN); - } + private static class ServerLevelAdvice { + @Advice.OnMethodEnter + static void enter( + @Advice.Argument(0) MinecraftServer server, + @Advice.Argument(3) PrimaryLevelData levelData, + @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem, + @Advice.Argument(12) World.Environment env, + @Advice.Argument(value = 13) ChunkGenerator gen + ) { + if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) + return; - private void copy(MappedRegistry target, Registry source) { - if (source == null) return; - source.listElementIds().forEach(key -> { - var value = source.getValue(key); - var info = source.registrationInfo(key).orElse(null); - if (value != null && info != null && !target.containsKey(key)) - target.register(key, value, info); - }); - } + try { + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class) + .invoke(bindings, server.registryAccess(), gen); - private ResourceLocation createIrisKey(ResourceKey key) { - return ResourceLocation.fromNamespaceAndPath("iris", key.location().getPath()); + levelData.customDimensions = null; + } catch (Throwable e) { + throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e); + } + } } } From cc27e8737650988e4957442ffdd38862fca066b2 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Thu, 5 Jun 2025 20:34:25 +0200 Subject: [PATCH 050/138] fix world height decreases deleting the whole chunk --- .../iris/core/nms/v1_20_R1/NMSBinding.java | 19 ++++++++++++++++-- .../iris/core/nms/v1_20_R2/NMSBinding.java | 19 ++++++++++++++++-- .../iris/core/nms/v1_20_R3/NMSBinding.java | 19 ++++++++++++++++-- .../iris/core/nms/v1_20_R4/NMSBinding.java | 19 ++++++++++++++++-- .../iris/core/nms/v1_21_R1/NMSBinding.java | 20 +++++++++++++++++-- .../iris/core/nms/v1_21_R2/NMSBinding.java | 20 +++++++++++++++++-- .../iris/core/nms/v1_21_R3/NMSBinding.java | 20 +++++++++++++++++-- .../iris/core/nms/v1_21_R4/NMSBinding.java | 13 ++++++++++++ 8 files changed, 135 insertions(+), 14 deletions(-) diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java index 8bb4d8182..9b28f5456 100644 --- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java @@ -50,6 +50,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; @@ -655,14 +656,21 @@ public class NMSBinding implements INMSBinding { return true; try { Iris.info("Injecting Bukkit"); - new ByteBuddy() - .redefine(ServerLevel.class) + var buddy = new ByteBuddy(); + buddy.redefine(ServerLevel.class) .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) .make() .load(ServerLevel.class.getClassLoader(), Agent.installed()); + for (Class clazz : List.of(ChunkAccess.class, ProtoChunk.class)) { + buddy.redefine(clazz) + .visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class)))) + .make() + .load(clazz.getClassLoader(), Agent.installed()); + } + return true; } catch (Throwable e) { Iris.error(C.RED + "Failed to inject Bukkit"); @@ -687,6 +695,13 @@ public class NMSBinding implements INMSBinding { return new FlatLevelSource(settings); } + private static class ChunkAccessAdvice { + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) { + return index >= access.getPostProcessing().length; + } + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter( diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java index 00224f458..99187723f 100644 --- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java @@ -51,6 +51,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; @@ -658,14 +659,21 @@ public class NMSBinding implements INMSBinding { return true; try { Iris.info("Injecting Bukkit"); - new ByteBuddy() - .redefine(ServerLevel.class) + var buddy = new ByteBuddy(); + buddy.redefine(ServerLevel.class) .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) .make() .load(ServerLevel.class.getClassLoader(), Agent.installed()); + for (Class clazz : List.of(ChunkAccess.class, ProtoChunk.class)) { + buddy.redefine(clazz) + .visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class)))) + .make() + .load(clazz.getClassLoader(), Agent.installed()); + } + return true; } catch (Throwable e) { Iris.error(C.RED + "Failed to inject Bukkit"); @@ -690,6 +698,13 @@ public class NMSBinding implements INMSBinding { return new FlatLevelSource(settings); } + private static class ChunkAccessAdvice { + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) { + return index >= access.getPostProcessing().length; + } + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter( diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java index fb47198fc..c1680fb93 100644 --- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java @@ -51,6 +51,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; @@ -659,14 +660,21 @@ public class NMSBinding implements INMSBinding { return true; try { Iris.info("Injecting Bukkit"); - new ByteBuddy() - .redefine(ServerLevel.class) + var buddy = new ByteBuddy(); + buddy.redefine(ServerLevel.class) .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) .make() .load(ServerLevel.class.getClassLoader(), Agent.installed()); + for (Class clazz : List.of(ChunkAccess.class, ProtoChunk.class)) { + buddy.redefine(clazz) + .visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class)))) + .make() + .load(clazz.getClassLoader(), Agent.installed()); + } + return true; } catch (Throwable e) { Iris.error(C.RED + "Failed to inject Bukkit"); @@ -691,6 +699,13 @@ public class NMSBinding implements INMSBinding { return new FlatLevelSource(settings); } + private static class ChunkAccessAdvice { + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) { + return index >= access.getPostProcessing().length; + } + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter( diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java index 14e02eabc..1ce8b16b4 100644 --- a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java @@ -51,6 +51,7 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; @@ -677,14 +678,21 @@ public class NMSBinding implements INMSBinding { return true; try { Iris.info("Injecting Bukkit"); - new ByteBuddy() - .redefine(ServerLevel.class) + var buddy = new ByteBuddy(); + buddy.redefine(ServerLevel.class) .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) .make() .load(ServerLevel.class.getClassLoader(), Agent.installed()); + for (Class clazz : List.of(ChunkAccess.class, ProtoChunk.class)) { + buddy.redefine(clazz) + .visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class)))) + .make() + .load(clazz.getClassLoader(), Agent.installed()); + } + return true; } catch (Throwable e) { Iris.error(C.RED + "Failed to inject Bukkit"); @@ -709,6 +717,13 @@ public class NMSBinding implements INMSBinding { return new FlatLevelSource(settings); } + private static class ChunkAccessAdvice { + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) { + return index >= access.getPostProcessing().length; + } + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter( diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java index 035f2cbb6..485b44e0c 100644 --- a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java +++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java @@ -23,6 +23,7 @@ import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.shorts.ShortList; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; @@ -53,6 +54,7 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.WorldGenContext; import net.minecraft.world.level.dimension.LevelStem; @@ -687,14 +689,21 @@ public class NMSBinding implements INMSBinding { return true; try { Iris.info("Injecting Bukkit"); - new ByteBuddy() - .redefine(ServerLevel.class) + var buddy = new ByteBuddy(); + buddy.redefine(ServerLevel.class) .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) .make() .load(ServerLevel.class.getClassLoader(), Agent.installed()); + for (Class clazz : List.of(ChunkAccess.class, ProtoChunk.class)) { + buddy.redefine(clazz) + .visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(short.class, int.class)))) + .make() + .load(clazz.getClassLoader(), Agent.installed()); + } + return true; } catch (Throwable e) { Iris.error(C.RED + "Failed to inject Bukkit"); @@ -719,6 +728,13 @@ public class NMSBinding implements INMSBinding { return new FlatLevelSource(settings); } + private static class ChunkAccessAdvice { + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) { + return index >= access.getPostProcessing().length; + } + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter( diff --git a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java index 93019eb7b..dff3db099 100644 --- a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java +++ b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java @@ -22,6 +22,7 @@ import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.shorts.ShortList; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; @@ -50,6 +51,7 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.WorldGenContext; import net.minecraft.world.level.dimension.LevelStem; @@ -684,14 +686,21 @@ public class NMSBinding implements INMSBinding { return true; try { Iris.info("Injecting Bukkit"); - new ByteBuddy() - .redefine(ServerLevel.class) + var buddy = new ByteBuddy(); + buddy.redefine(ServerLevel.class) .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) .make() .load(ServerLevel.class.getClassLoader(), Agent.installed()); + for (Class clazz : List.of(ChunkAccess.class, ProtoChunk.class)) { + buddy.redefine(clazz) + .visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class)))) + .make() + .load(clazz.getClassLoader(), Agent.installed()); + } + return true; } catch (Throwable e) { Iris.error(C.RED + "Failed to inject Bukkit"); @@ -716,6 +725,13 @@ public class NMSBinding implements INMSBinding { return new FlatLevelSource(settings); } + private static class ChunkAccessAdvice { + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) { + return index >= access.getPostProcessing().length; + } + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter( diff --git a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java index cf02a9ddb..ca046a955 100644 --- a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java +++ b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java @@ -23,6 +23,7 @@ import com.volmit.iris.util.nbt.mca.palette.*; import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.shorts.ShortList; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; @@ -51,6 +52,7 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.WorldGenContext; import net.minecraft.world.level.dimension.LevelStem; @@ -684,14 +686,21 @@ public class NMSBinding implements INMSBinding { return true; try { Iris.info("Injecting Bukkit"); - new ByteBuddy() - .redefine(ServerLevel.class) + var buddy = new ByteBuddy(); + buddy.redefine(ServerLevel.class) .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) .make() .load(ServerLevel.class.getClassLoader(), Agent.installed()); + for (Class clazz : List.of(ChunkAccess.class, ProtoChunk.class)) { + buddy.redefine(clazz) + .visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class)))) + .make() + .load(clazz.getClassLoader(), Agent.installed()); + } + return true; } catch (Throwable e) { Iris.error(C.RED + "Failed to inject Bukkit"); @@ -716,6 +725,13 @@ public class NMSBinding implements INMSBinding { return new FlatLevelSource(settings); } + private static class ChunkAccessAdvice { + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) { + return index >= access.getPostProcessing().length; + } + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter( diff --git a/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/NMSBinding.java b/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/NMSBinding.java index 6766d2ab8..a376cb83d 100644 --- a/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/NMSBinding.java +++ b/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/NMSBinding.java @@ -694,6 +694,12 @@ public class NMSBinding implements INMSBinding { boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) .make() .load(ServerLevel.class.getClassLoader(), Agent.installed()); + for (Class clazz : List.of(ChunkAccess.class, ProtoChunk.class)) { + buddy.redefine(clazz) + .visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class)))) + .make() + .load(clazz.getClassLoader(), Agent.installed()); + } return true; } catch (Throwable e) { @@ -719,6 +725,13 @@ public class NMSBinding implements INMSBinding { return new FlatLevelSource(settings); } + private static class ChunkAccessAdvice { + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) { + return index >= access.getPostProcessing().length; + } + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter( From 2ee22db0728fc387385329fcc233ee9b44b82b91 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 6 Jun 2025 16:49:42 +0200 Subject: [PATCH 051/138] fix mantle write failing on windows --- .../java/com/volmit/iris/util/mantle/Mantle.java | 4 +++- .../volmit/iris/util/mantle/TectonicPlate.java | 15 +++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java index 77b849272..9de145a3f 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -34,6 +34,7 @@ import com.volmit.iris.util.documentation.RegionCoordinates; import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; import com.volmit.iris.util.function.Consumer4; +import com.volmit.iris.util.io.IO; import com.volmit.iris.util.math.M; import com.volmit.iris.util.matter.Matter; import com.volmit.iris.util.matter.MatterSlice; @@ -84,7 +85,7 @@ public class Mantle { this.worldHeight = worldHeight; this.ioTrim = new AtomicBoolean(false); this.ioTectonicUnload = new AtomicBoolean(false); - dataFolder.mkdirs(); + new File(dataFolder, ".tmp").mkdirs(); loadedRegions = new KMap<>(); lastUse = new KMap<>(); ioBurst = MultiBurst.burst; @@ -387,6 +388,7 @@ public class Mantle { } })); loadedRegions.clear(); + IO.delete(new File(dataFolder, ".tmp")); try { b.complete(); diff --git a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java index 82c259aaf..009993321 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java @@ -222,13 +222,16 @@ public class TectonicPlate { */ public void write(File file) throws IOException { PrecisionStopwatch p = PrecisionStopwatch.start(); - File temp = File.createTempFile("iris-tectonic-plate", ".bin"); - try (DataOutputStream dos = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(temp)))) { - write(dos); + File temp = File.createTempFile("iris-tectonic-plate", ".bin", new File(file.getParentFile(), ".tmp")); + try { + try (DataOutputStream dos = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(temp)))) { + write(dos); + } + Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); + Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); + } finally { + temp.delete(); } - Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); - Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); - temp.delete(); } /** From 52f87befa21f6ef36060fdd482df438529b5bd6a Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 6 Jun 2025 17:24:23 +0200 Subject: [PATCH 052/138] return noop pregen cache when the service is disabled and write pregen cache to temp file first then replace real one --- .../pregenerator/cache/PregenCacheImpl.java | 5 +++-- .../iris/core/service/GlobalCacheSVC.java | 6 +++++- .../com/volmit/iris/util/collection/KMap.java | 20 +++++++++++++++---- .../main/java/com/volmit/iris/util/io/IO.java | 19 ++++++++++++++++++ 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java index bf48e06e9..c3404eda5 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java @@ -7,6 +7,7 @@ import com.volmit.iris.Iris; import com.volmit.iris.util.data.Varint; import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.documentation.RegionCoordinates; +import com.volmit.iris.util.io.IO; import com.volmit.iris.util.parallel.HyperLock; import lombok.RequiredArgsConstructor; import net.jpountz.lz4.LZ4BlockInputStream; @@ -82,8 +83,8 @@ class PregenCacheImpl implements PregenCache { hyperLock.lock(plate.pos.x, plate.pos.z); try { File file = fileForPlate(plate.pos); - try (var out = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(file)))) { - plate.write(out); + try { + IO.write(file, out -> new DataOutputStream(new LZ4BlockOutputStream(out)), plate::write); } catch (IOException e) { Iris.error("Failed to write pregen cache " + file); e.printStackTrace(); diff --git a/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java b/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java index 32d6af184..e8c194974 100644 --- a/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java @@ -23,9 +23,11 @@ public class GlobalCacheSVC implements IrisService { private static final Cache REFERENCE_CACHE = Caffeine.newBuilder().weakValues().build(); private final KMap globalCache = new KMap<>(); private transient boolean lastState; + private static boolean disabled = true; @Override public void onEnable() { + disabled = false; lastState = !IrisSettings.get().getWorld().isGlobalPregenCache(); if (lastState) return; Bukkit.getWorlds().forEach(this::createCache); @@ -33,7 +35,8 @@ public class GlobalCacheSVC implements IrisService { @Override public void onDisable() { - globalCache.values().forEach(PregenCache::write); + disabled = true; + globalCache.qclear((world, cache) -> cache.write()); } @Nullable @@ -99,6 +102,7 @@ public class GlobalCacheSVC implements IrisService { } private static PregenCache createDefault0(String worldName) { + if (disabled) return PregenCache.EMPTY; return PregenCache.create(new File(Bukkit.getWorldContainer(), String.join(File.separator, worldName, "iris", "pregen"))).sync(); } } diff --git a/core/src/main/java/com/volmit/iris/util/collection/KMap.java b/core/src/main/java/com/volmit/iris/util/collection/KMap.java index 01baab32b..4a7f938e0 100644 --- a/core/src/main/java/com/volmit/iris/util/collection/KMap.java +++ b/core/src/main/java/com/volmit/iris/util/collection/KMap.java @@ -23,11 +23,9 @@ import com.volmit.iris.util.function.Consumer2; import com.volmit.iris.util.function.Consumer3; import com.volmit.iris.util.scheduling.Queue; -import java.util.Collections; -import java.util.Comparator; -import java.util.Enumeration; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; import java.util.function.BiFunction; @SuppressWarnings("ALL") @@ -373,6 +371,20 @@ public class KMap extends ConcurrentHashMap { return g; } + public KMap qclear(BiConsumer action) { + final Iterator> it = entrySet().iterator(); + while (it.hasNext()) { + final Map.Entry entry = it.next(); + it.remove(); + try { + action.accept(entry.getKey(), entry.getValue()); + } catch (Throwable e) { + Iris.reportError(e); + } + } + return this; + } + /** * Create a keypair queue * diff --git a/core/src/main/java/com/volmit/iris/util/io/IO.java b/core/src/main/java/com/volmit/iris/util/io/IO.java index 554058244..281f08096 100644 --- a/core/src/main/java/com/volmit/iris/util/io/IO.java +++ b/core/src/main/java/com/volmit/iris/util/io/IO.java @@ -24,9 +24,13 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.volmit.iris.Iris; import com.volmit.iris.util.format.Form; +import org.apache.commons.io.function.IOConsumer; +import org.apache.commons.io.function.IOFunction; import java.io.*; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -1638,4 +1642,19 @@ public class IO { int ch2 = input2.read(); return (ch2 == -1); } + + public static void write(File file, IOFunction builder, IOConsumer action) throws IOException { + File dir = new File(file.getParentFile(), ".tmp"); + dir.mkdirs(); + dir.deleteOnExit(); + File temp = File.createTempFile("iris",".bin", dir); + try { + try (var out = builder.apply(new FileOutputStream(temp))) { + action.accept(out); + } + Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); + } finally { + temp.delete(); + } + } } From 329e136a66760b369011f86006bf1f13f37aa13b Mon Sep 17 00:00:00 2001 From: Julian Krings <47589149+CrazyDev05@users.noreply.github.com> Date: Fri, 6 Jun 2025 17:33:16 +0200 Subject: [PATCH 053/138] implement opt-out auto reporting with sentry (#1201) --- build.gradle | 10 ++++ core/src/main/java/com/volmit/iris/Iris.java | 40 +++++++++++++++- .../com/volmit/iris/core/IrisSettings.java | 7 +++ .../volmit/iris/core/link/WorldEditLink.java | 1 + .../com/volmit/iris/core/loader/IrisData.java | 1 + .../iris/core/pregenerator/ChunkUpdater.java | 2 + .../pregenerator/cache/PregenCacheImpl.java | 2 + .../methods/AsyncPregenMethod.java | 8 +++- .../iris/core/safeguard/IrisSafeguard.java | 10 ++++ .../volmit/iris/util/scheduling/Looper.java | 1 - .../volmit/iris/util/sentry/Attachments.java | 41 ++++++++++++++++ .../volmit/iris/util/sentry/IrisLogger.java | 47 +++++++++++++++++++ 12 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/util/sentry/Attachments.java create mode 100644 core/src/main/java/com/volmit/iris/util/sentry/IrisLogger.java diff --git a/build.gradle b/build.gradle index 88f7a3b94..45f2d09fd 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,7 @@ plugins { id "io.github.goooler.shadow" version "8.1.7" id "de.undercouch.download" version "5.0.1" id "xyz.jpenilla.run-paper" version "2.3.1" + id "io.sentry.jvm.gradle" version "5.7.0" } @@ -132,6 +133,7 @@ configurations.configureEach { allprojects { apply plugin: 'java' + apply plugin: 'io.sentry.jvm.gradle' repositories { mavenCentral() @@ -203,6 +205,14 @@ allprojects { archiveClassifier.set('javadoc') from javadoc.destinationDir } + + sentry { + includeSourceContext = true + + org = "volmit-software" + projectName = "iris" + authToken = property("sentry.auth.token") + } } if (JavaVersion.current().toString() != "21") { diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 5bf32b68f..34dd8ab68 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -32,7 +32,6 @@ import com.volmit.iris.core.nms.v1X.NMSBinding1X; import com.volmit.iris.core.pregenerator.LazyPregenerator; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.tools.IrisToolbelt; -import com.volmit.iris.core.tools.IrisWorldCreator; import com.volmit.iris.engine.EnginePanic; import com.volmit.iris.engine.object.IrisCompat; import com.volmit.iris.engine.object.IrisContextInjector; @@ -64,7 +63,10 @@ import com.volmit.iris.util.reflect.ShadeFix; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.Queue; import com.volmit.iris.util.scheduling.ShurikenQueue; +import com.volmit.iris.util.sentry.Attachments; +import com.volmit.iris.util.sentry.IrisLogger; import io.papermc.lib.PaperLib; +import io.sentry.Sentry; import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.serializer.ComponentSerializer; import org.bstats.bukkit.Metrics; @@ -392,6 +394,7 @@ public class Iris extends VolmitPlugin implements Listener { } public static void reportError(Throwable e) { + Sentry.captureException(e); if (IrisSettings.get().getGeneral().isDebug()) { String n = e.getClass().getCanonicalName() + "-" + e.getStackTrace()[0].getClassName() + "-" + e.getStackTrace()[0].getLineNumber(); @@ -456,6 +459,7 @@ public class Iris extends VolmitPlugin implements Listener { instance = this; services = new KMap<>(); setupAudience(); + setupSentry(); initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class) i.getClass(), (IrisService) i)); INMS.get(); IO.delete(new File("iris")); @@ -486,6 +490,12 @@ public class Iris extends VolmitPlugin implements Listener { checkForBukkitWorlds(); IrisToolbelt.retainMantleDataForSlice(String.class.getCanonicalName()); IrisToolbelt.retainMantleDataForSlice(BlockData.class.getCanonicalName()); + + try { + throw new Exception("This is a test exception"); + } catch (Exception e) { + reportError(e); + } }); } @@ -527,6 +537,7 @@ public class Iris extends VolmitPlugin implements Listener { } } catch (Throwable e) { e.printStackTrace(); + Sentry.captureException(e); } } @@ -544,7 +555,7 @@ public class Iris extends VolmitPlugin implements Listener { }); }); } catch (IrisException e) { - e.printStackTrace(); + Sentry.captureException(e); } } } @@ -940,4 +951,29 @@ public class Iris extends VolmitPlugin implements Listener { return -1; } } + + private static void setupSentry() { + var settings = IrisSettings.get().getSentry(); + if (settings.disableAutoReporting || Sentry.isEnabled()) return; + Sentry.init(options -> { + options.setDsn("https://9e190553a1aea18f1cab566cbc894d3f@o4509451052646400.ingest.de.sentry.io/4509451115036752"); + if (settings.debug) { + options.setLogger(new IrisLogger()); + options.setDebug(true); + } + + options.setBeforeSend((event, hint) -> { + event.setTag("iris.safeguard", IrisSafeguard.mode()); + event.setTag("iris.nms", INMS.get().getClass().getCanonicalName()); + return event; + }); + }); + Sentry.configureScope(scope -> { + scope.addAttachment(Attachments.PLUGINS); + scope.setTag("server", Bukkit.getVersion()); + scope.setTag("server.type", Bukkit.getName()); + scope.setTag("server.api", Bukkit.getBukkitVersion()); + }); + Runtime.getRuntime().addShutdownHook(new Thread(Sentry::close)); + } } diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index b7e5bae20..20c0fa265 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -46,6 +46,7 @@ public class IrisSettings { private IrisSettingsPerformance performance = new IrisSettingsPerformance(); private IrisSettingsUpdater updater = new IrisSettingsUpdater(); private IrisSettingsPregen pregen = new IrisSettingsPregen(); + private IrisSettingsSentry sentry = new IrisSettingsSentry(); public static int getThreadCount(int c) { return switch (c) { @@ -222,6 +223,12 @@ public class IrisSettings { } } + @Data + public static class IrisSettingsSentry { + public boolean disableAutoReporting = false; + public boolean debug = false; + } + @Data public static class IrisSettingsGUI { public boolean useServerLaunchedGuis = true; diff --git a/core/src/main/java/com/volmit/iris/core/link/WorldEditLink.java b/core/src/main/java/com/volmit/iris/core/link/WorldEditLink.java index 42e335a10..638c66cc2 100644 --- a/core/src/main/java/com/volmit/iris/core/link/WorldEditLink.java +++ b/core/src/main/java/com/volmit/iris/core/link/WorldEditLink.java @@ -47,6 +47,7 @@ public class WorldEditLink { } catch (Throwable e) { Iris.error("Could not get selection"); e.printStackTrace(); + Iris.reportError(e); active.reset(); active.aquire(() -> false); } diff --git a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java index e51c93176..a272be0d7 100644 --- a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java +++ b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java @@ -300,6 +300,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { return r; } catch (Throwable e) { + Iris.reportError(e); e.printStackTrace(); Iris.error("Failed to create loader! " + registrant.getCanonicalName()); } diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java b/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java index 9f9bde99e..572ce98d4 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java @@ -109,6 +109,7 @@ public class ChunkUpdater { } } } catch (Exception e) { + Iris.reportError(e); e.printStackTrace(); } }, 0, 3, TimeUnit.SECONDS); @@ -314,6 +315,7 @@ public class ChunkUpdater { world.save(); }).get(); } catch (Throwable e) { + Iris.reportError(e); e.printStackTrace(); } } diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java index c3404eda5..b9a3de943 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java @@ -71,6 +71,7 @@ class PregenCacheImpl implements PregenCache { return new Plate(key, in); } catch (IOException e){ Iris.error("Failed to read pregen cache " + file); + Iris.reportError(e); e.printStackTrace(); return new Plate(key); } @@ -87,6 +88,7 @@ class PregenCacheImpl implements PregenCache { IO.write(file, out -> new DataOutputStream(new LZ4BlockOutputStream(out)), plate::write); } catch (IOException e) { Iris.error("Failed to write pregen cache " + file); + Iris.reportError(e); e.printStackTrace(); } } finally { diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java index 757dfcf8f..0dcfffcfc 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java @@ -157,7 +157,10 @@ public class AsyncPregenMethod implements PregeneratorMethod { } catch (Throwable e) { Iris.warn("Failed to increase worker threads, if you are on paper or a fork of it please increase it manually to " + adjusted); Iris.warn("For more information see https://docs.papermc.io/paper/reference/global-configuration#chunk_system_worker_threads"); - if (e instanceof InvocationTargetException) e.printStackTrace(); + if (e instanceof InvocationTargetException) { + Iris.reportError(e); + e.printStackTrace(); + } } return 0; }); @@ -173,6 +176,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { method.invoke(pool, i); return 0; } catch (Throwable e) { + Iris.reportError(e); Iris.error("Failed to reset worker threads"); e.printStackTrace(); } @@ -201,6 +205,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { }).get(); } catch (InterruptedException ignored) { } catch (Throwable e) { + Iris.reportError(e); e.printStackTrace(); } finally { semaphore.release(); @@ -219,6 +224,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { public void generate(int x, int z, PregenListener listener) { PaperLib.getChunkAtAsync(world, x, z, true, urgent) .exceptionally(e -> { + Iris.reportError(e); e.printStackTrace(); return null; }) diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java b/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java index 5217be9f2..2c9eb9be1 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java @@ -20,5 +20,15 @@ public class IrisSafeguard { Iris.instance.splash(); UtilsSFG.splash(); } + + public static String mode() { + if (unstablemode) { + return "unstable"; + } else if (warningmode) { + return "warning"; + } else { + return "stable"; + } + } } diff --git a/core/src/main/java/com/volmit/iris/util/scheduling/Looper.java b/core/src/main/java/com/volmit/iris/util/scheduling/Looper.java index e5be20b84..72ba9ff03 100644 --- a/core/src/main/java/com/volmit/iris/util/scheduling/Looper.java +++ b/core/src/main/java/com/volmit/iris/util/scheduling/Looper.java @@ -36,7 +36,6 @@ public abstract class Looper extends Thread { //noinspection BusyWait Thread.sleep(m); } catch (InterruptedException e) { - Iris.reportError(e); break; } catch (Throwable e) { Iris.reportError(e); diff --git a/core/src/main/java/com/volmit/iris/util/sentry/Attachments.java b/core/src/main/java/com/volmit/iris/util/sentry/Attachments.java new file mode 100644 index 000000000..a338ea6ce --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/sentry/Attachments.java @@ -0,0 +1,41 @@ +package com.volmit.iris.util.sentry; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.volmit.iris.util.collection.KMap; +import io.sentry.Attachment; +import org.bukkit.Bukkit; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.Callable; + +public class Attachments { + private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create(); + public static final Attachment PLUGINS = jsonProvider(Attachments::plugins, "plugins.json"); + + public static Attachment json(Object object, String name) { + return new Attachment(GSON.toJson(object).getBytes(StandardCharsets.UTF_8), name, "application/json", "event.attachment", true); + } + + public static Attachment jsonProvider(Callable object, String name) { + return new Attachment(() -> GSON.toJson(object.call()).getBytes(StandardCharsets.UTF_8), name, "application/json", "event.attachment", true); + } + + private static KMap plugins() { + KMap enabled = new KMap<>(); + KMap disabled = new KMap<>(); + + var pm = Bukkit.getPluginManager(); + for (var plugin : pm.getPlugins()) { + if (plugin.isEnabled()) { + enabled.put(plugin.getName(), plugin.getDescription().getVersion()); + } else { + disabled.put(plugin.getName(), plugin.getDescription().getVersion()); + } + } + + return new KMap() + .qput("enabled", enabled) + .qput("disabled", disabled); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/sentry/IrisLogger.java b/core/src/main/java/com/volmit/iris/util/sentry/IrisLogger.java new file mode 100644 index 000000000..e51d407c5 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/sentry/IrisLogger.java @@ -0,0 +1,47 @@ +package com.volmit.iris.util.sentry; + +import com.volmit.iris.Iris; +import io.sentry.ILogger; +import io.sentry.SentryLevel; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.PrintWriter; +import java.io.StringWriter; + +public class IrisLogger implements ILogger { + @Override + public void log(@NotNull SentryLevel level, @NotNull String message, @Nullable Object... args) { + Iris.msg(String.format("%s: %s", level, String.format(message, args))); + } + + @Override + public void log(@NotNull SentryLevel level, @NotNull String message, @Nullable Throwable throwable) { + if (throwable == null) { + log(level, message); + } else { + Iris.msg(String.format("%s: %s\n%s", level, String.format(message, throwable), captureStackTrace(throwable))); + } + } + + @Override + public void log(@NotNull SentryLevel level, @Nullable Throwable throwable, @NotNull String message, @Nullable Object... args) { + if (throwable == null) { + log(level, message, args); + } else { + Iris.msg(String.format("%s: %s\n%s", level, String.format(message, throwable), captureStackTrace(throwable))); + } + } + + @Override + public boolean isEnabled(@Nullable SentryLevel level) { + return true; + } + + private @NotNull String captureStackTrace(@NotNull Throwable throwable) { + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + throwable.printStackTrace(printWriter); + return stringWriter.toString(); + } +} From 67328d7d10725fb36578700108dedf1878dce4f5 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 6 Jun 2025 17:35:13 +0200 Subject: [PATCH 054/138] remove test exception for sentry --- core/src/main/java/com/volmit/iris/Iris.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 34dd8ab68..77717d797 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -490,12 +490,6 @@ public class Iris extends VolmitPlugin implements Listener { checkForBukkitWorlds(); IrisToolbelt.retainMantleDataForSlice(String.class.getCanonicalName()); IrisToolbelt.retainMantleDataForSlice(BlockData.class.getCanonicalName()); - - try { - throw new Exception("This is a test exception"); - } catch (Exception e) { - reportError(e); - } }); } From c767b6c8e810c478e72124bff1ff0cb09f3228b9 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 6 Jun 2025 18:36:41 +0200 Subject: [PATCH 055/138] check sentry env token as fallback --- build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 45f2d09fd..a564a0dbc 100644 --- a/build.gradle +++ b/build.gradle @@ -211,7 +211,9 @@ allprojects { org = "volmit-software" projectName = "iris" - authToken = property("sentry.auth.token") + authToken = hasProperty("sentry.auth.token") ? + property("sentry.auth.token") : + System.getenv("SENTRY_AUTH_TOKEN") } } From ce2b62f5ae2f8444d84c5cdd32d0dd5fa00c0e68 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 6 Jun 2025 18:37:11 +0200 Subject: [PATCH 056/138] add sentry release task --- build.gradle | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/build.gradle b/build.gradle index a564a0dbc..3f49995df 100644 --- a/build.gradle +++ b/build.gradle @@ -291,3 +291,39 @@ def registerCustomOutputTaskUnix(name, path) { } tasks.build.dependsOn(shadowJar) + +def cli = file("sentry-cli.exe") +tasks.register("downloadCli", Download) { + group = "sentry" + src "https://release-registry.services.sentry.io/apps/sentry-cli/latest?response=download&arch=x86_64&platform=${System.getProperty("os.name")}&package=sentry-cli" + dest cli + + doLast { + cli.setExecutable(true) + } +} + +tasks.register("release") { + group = "sentry" + dependsOn("downloadCli") + doLast { + def authToken = project.hasProperty("sentry.auth.token") ? + project.property("sentry.auth.token") : + System.getenv("SENTRY_AUTH_TOKEN") + def org = "volmit-software" + def projectName = "iris" + exec { + executable(cli) + args("releases", "new", "--auth-token", authToken, "-o", org, "-p", projectName, version) + } + exec { + executable(cli) + args("releases", "set-commits", "--auth-token", authToken, "-o", org, "-p", projectName, version, "--auto") + } + exec { + executable(cli) + args("releases", "finalize", "--auth-token", authToken, "-o", org, "-p", projectName, version) + } + cli.delete() + } +} \ No newline at end of file From b66e6d83352f1d806120f861b78d73621de7466e Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 7 Jun 2025 14:00:22 +0200 Subject: [PATCH 057/138] correct sentry dsn --- core/src/main/java/com/volmit/iris/Iris.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 77717d797..6a4a4e0d7 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -490,6 +490,8 @@ public class Iris extends VolmitPlugin implements Listener { checkForBukkitWorlds(); IrisToolbelt.retainMantleDataForSlice(String.class.getCanonicalName()); IrisToolbelt.retainMantleDataForSlice(BlockData.class.getCanonicalName()); + + reportError(new Exception("This is a test")); }); } @@ -950,7 +952,7 @@ public class Iris extends VolmitPlugin implements Listener { var settings = IrisSettings.get().getSentry(); if (settings.disableAutoReporting || Sentry.isEnabled()) return; Sentry.init(options -> { - options.setDsn("https://9e190553a1aea18f1cab566cbc894d3f@o4509451052646400.ingest.de.sentry.io/4509451115036752"); + options.setDsn("https://b16ecc222e9c1e0c48faecacb906fd89@o4509451052646400.ingest.de.sentry.io/4509452722765904"); if (settings.debug) { options.setLogger(new IrisLogger()); options.setDebug(true); From 811920725433782ebb7c5378fdfe9dfe83e3838e Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 8 Jun 2025 12:29:24 +0200 Subject: [PATCH 058/138] sentry changes - info message on enabling - add release tag - disable uncaught exception handling to prevent reporting other plugins issues --- core/src/main/java/com/volmit/iris/Iris.java | 9 +++++---- .../com/volmit/iris/core/commands/CommandDeveloper.java | 5 +++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 6a4a4e0d7..35a6e6ab6 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -490,8 +490,6 @@ public class Iris extends VolmitPlugin implements Listener { checkForBukkitWorlds(); IrisToolbelt.retainMantleDataForSlice(String.class.getCanonicalName()); IrisToolbelt.retainMantleDataForSlice(BlockData.class.getCanonicalName()); - - reportError(new Exception("This is a test")); }); } @@ -533,7 +531,7 @@ public class Iris extends VolmitPlugin implements Listener { } } catch (Throwable e) { e.printStackTrace(); - Sentry.captureException(e); + reportError(e); } } @@ -551,7 +549,7 @@ public class Iris extends VolmitPlugin implements Listener { }); }); } catch (IrisException e) { - Sentry.captureException(e); + reportError(e); } } } @@ -951,6 +949,7 @@ public class Iris extends VolmitPlugin implements Listener { private static void setupSentry() { var settings = IrisSettings.get().getSentry(); if (settings.disableAutoReporting || Sentry.isEnabled()) return; + Iris.info("Enabling Sentry for anonymous error reporting. You can disable this in the settings."); Sentry.init(options -> { options.setDsn("https://b16ecc222e9c1e0c48faecacb906fd89@o4509451052646400.ingest.de.sentry.io/4509452722765904"); if (settings.debug) { @@ -958,6 +957,8 @@ public class Iris extends VolmitPlugin implements Listener { options.setDebug(true); } + options.setEnableUncaughtExceptionHandler(false); + options.setRelease(Iris.instance.getDescription().getVersion()); options.setBeforeSend((event, hint) -> { event.setTag("iris.safeguard", IrisSafeguard.mode()); event.setTag("iris.nms", INMS.get().getClass().getCanonicalName()); diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java index 5d6090b00..29d4fd80f 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java @@ -71,6 +71,11 @@ public class CommandDeveloper implements DecreeExecutor { .engineStatus(sender()); } + @Decree(description = "Send a test exception to sentry") + public void Sentry() { + Iris.reportError(new Exception("This is a test")); + } + @Decree(description = "Test") public void dumpThreads() { try { From 0d103a934aa68400544d6acc6f715cea0387b3cd Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 8 Jun 2025 12:43:56 +0200 Subject: [PATCH 059/138] fix ticking engine players in the wrong dimension --- .../iris/engine/framework/EnginePlayer.java | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/framework/EnginePlayer.java b/core/src/main/java/com/volmit/iris/engine/framework/EnginePlayer.java index 803819831..2350b3140 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/EnginePlayer.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/EnginePlayer.java @@ -47,9 +47,7 @@ public class EnginePlayer { } public void tick() { - sample(); - - if (!IrisSettings.get().getWorld().isEffectSystem()) + if (sample() || !IrisSettings.get().getWorld().isEffectSystem()) return; J.a(() -> { @@ -81,22 +79,22 @@ public class EnginePlayer { return M.ms() - lastSample; } - public void sample() { + public boolean sample() { + Location current = player.getLocation().clone(); + if (current.getWorld() != engine.getWorld().realWorld()) + return true; try { - if (ticksSinceLastSample() > 55 && player.getLocation().distanceSquared(lastLocation) > 9 * 9) { - lastLocation = player.getLocation().clone(); + if (ticksSinceLastSample() > 55 && current.distanceSquared(lastLocation) > 9 * 9) { + lastLocation = current; lastSample = M.ms(); - sampleBiomeRegion(); + biome = engine.getBiome(current); + region = engine.getRegion(current); } + return false; } catch (Throwable e) { Iris.reportError(e); } - } - - private void sampleBiomeRegion() { - Location l = player.getLocation(); - biome = engine.getBiome(l); - region = engine.getRegion(l); + return true; } } From e63d84c052253f4e6a6f0f59b65ec01ce69cac5c Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 00:16:58 +0200 Subject: [PATCH 060/138] remove server name from reports --- core/src/main/java/com/volmit/iris/Iris.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 35a6e6ab6..43384f127 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -957,6 +957,7 @@ public class Iris extends VolmitPlugin implements Listener { options.setDebug(true); } + options.setAttachServerName(false); options.setEnableUncaughtExceptionHandler(false); options.setRelease(Iris.instance.getDescription().getVersion()); options.setBeforeSend((event, hint) -> { From fdaf8ff9d3332619d3c057cf74f7475f60675eba Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 00:17:38 +0200 Subject: [PATCH 061/138] relocate sentry to fix incompatibility with some plugins using a very old sentry version --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 3f49995df..1d1e26ea0 100644 --- a/build.gradle +++ b/build.gradle @@ -114,6 +114,7 @@ shadowJar { relocate 'io.papermc.lib', 'com.volmit.iris.util.paper' relocate 'net.kyori', 'com.volmit.iris.util.kyori' relocate 'org.bstats', 'com.volmit.util.metrics' + relocate "io.sentry", "com.volmit.iris.util.sentry" archiveFileName.set("Iris-${project.version}.jar") dependencies { From b341089996eb64687199fcf5bd9dbd4a9e880627 Mon Sep 17 00:00:00 2001 From: Julian Krings <47589149+CrazyDev05@users.noreply.github.com> Date: Mon, 9 Jun 2025 02:12:14 +0200 Subject: [PATCH 062/138] v+ --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 1d1e26ea0..5036603cd 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ plugins { } -version '3.6.9-1.20.1-1.21.5' +version '3.6.10-1.20.1-1.21.5' // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED // ======================== WINDOWS ============================= @@ -327,4 +327,4 @@ tasks.register("release") { } cli.delete() } -} \ No newline at end of file +} From a88d389e0f42f95386d4853fab1fa44feaa92374 Mon Sep 17 00:00:00 2001 From: Julian Krings <47589149+CrazyDev05@users.noreply.github.com> Date: Mon, 9 Jun 2025 12:45:27 +0200 Subject: [PATCH 063/138] bump gradle wrapper to 8.14.2 and switch to kotlin dsl (#1203) --- build.gradle | 330 ----------------------- build.gradle.kts | 276 +++++++++++++++++++ core/build.gradle | 95 ------- core/build.gradle.kts | 151 +++++++++++ core/src/main/resources/plugin.yml | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 41 ++- gradlew.bat | 35 +-- settings.gradle => settings.gradle.kts | 22 +- 10 files changed, 489 insertions(+), 467 deletions(-) delete mode 100644 build.gradle create mode 100644 build.gradle.kts delete mode 100644 core/build.gradle create mode 100644 core/build.gradle.kts rename settings.gradle => settings.gradle.kts (74%) diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 5036603cd..000000000 --- a/build.gradle +++ /dev/null @@ -1,330 +0,0 @@ -import xyz.jpenilla.runpaper.task.RunServer - -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2021 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 . - */ - -buildscript() { - repositories { - maven { url 'https://jitpack.io'} - } - dependencies { - classpath 'com.github.VolmitSoftware:NMSTools:1.0.1' - } -} - -plugins { - id 'java' - id 'java-library' - id "io.github.goooler.shadow" version "8.1.7" - id "de.undercouch.download" version "5.0.1" - id "xyz.jpenilla.run-paper" version "2.3.1" - id "io.sentry.jvm.gradle" version "5.7.0" -} - - -version '3.6.10-1.20.1-1.21.5' - -// ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED -// ======================== WINDOWS ============================= -registerCustomOutputTask('Cyberpwn', 'C://Users/cyberpwn/Documents/development/server/plugins') -registerCustomOutputTask('Psycho', 'C://Dan/MinecraftDevelopment/Server/plugins') -registerCustomOutputTask('ArcaneArts', 'C://Users/arcane/Documents/development/server/plugins') -registerCustomOutputTask('Coco', 'D://mcsm/plugins') -registerCustomOutputTask('Strange', 'D://Servers/1.17 Test Server/plugins') -registerCustomOutputTask('Vatuu', 'D://Minecraft/Servers/1.19.4/plugins') -registerCustomOutputTask('CrazyDev22', 'C://Users/Julian/Desktop/server/plugins') -registerCustomOutputTask('PixelFury', 'C://Users/repix/workplace/Iris/1.21.3 - Development-Public-v3/plugins') -registerCustomOutputTask('PixelFuryDev', 'C://Users/repix/workplace/Iris/1.21 - Development-v3/plugins') -// ========================== UNIX ============================== -registerCustomOutputTaskUnix('CyberpwnLT', '/Users/danielmills/development/server/plugins') -registerCustomOutputTaskUnix('PsychoLT', '/Users/brianfopiano/Developer/RemoteGit/Server/plugins') -registerCustomOutputTaskUnix('PixelMac', '/Users/test/Desktop/mcserver/plugins') -registerCustomOutputTaskUnix('CrazyDev22LT', '/home/julian/Desktop/server/plugins') -// ============================================================== - -def MIN_HEAP_SIZE = "2G" -def MAX_HEAP_SIZE = "8G" -//Valid values are: none, truecolor, indexed256, indexed16, indexed8 -def COLOR = "truecolor" - -def NMS_BINDINGS = Map.of( - "v1_21_R4", "1.21.5-R0.1-SNAPSHOT", - "v1_21_R3", "1.21.4-R0.1-SNAPSHOT", - "v1_21_R2", "1.21.3-R0.1-SNAPSHOT", - "v1_21_R1", "1.21.1-R0.1-SNAPSHOT", - "v1_20_R4", "1.20.6-R0.1-SNAPSHOT", - "v1_20_R3", "1.20.4-R0.1-SNAPSHOT", - "v1_20_R2", "1.20.2-R0.1-SNAPSHOT", - "v1_20_R1", "1.20.1-R0.1-SNAPSHOT", -) -def JVM_VERSION = Map.of() -NMS_BINDINGS.forEach { key, value -> - project(":nms:$key") { - apply plugin: 'java' - apply plugin: 'com.volmit.nmstools' - - nmsTools { - it.jvm = JVM_VERSION.getOrDefault(key, 21) - it.version = value - } - - dependencies { - implementation project(":core") - } - } - - tasks.register("runServer-$key", RunServer) { - group("servers") - minecraftVersion(value.split("-")[0]) - minHeapSize(MIN_HEAP_SIZE) - maxHeapSize(MAX_HEAP_SIZE) - pluginJars(tasks.shadowJar.archiveFile) - javaLauncher = javaToolchains.launcherFor { it.languageVersion = JavaLanguageVersion.of(JVM_VERSION.getOrDefault(key, 21))} - runDirectory.convention(layout.buildDirectory.dir("run/$key")) - systemProperty("disable.watchdog", "") - systemProperty("net.kyori.ansi.colorLevel", COLOR) - systemProperty("com.mojang.eula.agree", true) - } -} - -shadowJar { - NMS_BINDINGS.each { - dependsOn(":nms:${it.key}:remap") - from("${project(":nms:${it.key}").layout.buildDirectory.asFile.get()}/libs/${it.key}-mapped.jar") - } - - //minimize() - append("plugin.yml") - relocate 'com.dfsek.paralithic', 'com.volmit.iris.util.paralithic' - relocate 'io.papermc.lib', 'com.volmit.iris.util.paper' - relocate 'net.kyori', 'com.volmit.iris.util.kyori' - relocate 'org.bstats', 'com.volmit.util.metrics' - relocate "io.sentry", "com.volmit.iris.util.sentry" - archiveFileName.set("Iris-${project.version}.jar") - - dependencies { - exclude(dependency("org.ow2.asm:asm:")) - exclude(dependency("org.jetbrains:")) - } -} - -dependencies { - implementation project(':core') -} - -configurations.configureEach { - resolutionStrategy.cacheChangingModulesFor 60, 'minutes' - resolutionStrategy.cacheDynamicVersionsFor 60, 'minutes' -} - -allprojects { - apply plugin: 'java' - apply plugin: 'io.sentry.jvm.gradle' - - repositories { - mavenCentral() - maven { url "https://repo.papermc.io/repository/maven-public/" } - maven { url "https://repo.codemc.org/repository/maven-public" } - maven { url "https://mvn.lumine.io/repository/maven-public/" } - maven { url "https://jitpack.io" } - - maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots" } - maven { url "https://mvn.lumine.io/repository/maven/" } - maven { url "https://repo.triumphteam.dev/snapshots" } - maven { url "https://repo.mineinabyss.com/releases" } - maven { url 'https://hub.jeff-media.com/nexus/repository/jeff-media-public/' } - maven { url "https://repo.nexomc.com/releases/" } - maven { url "https://libraries.minecraft.net" } - } - - dependencies { - // Provided or Classpath - compileOnly 'org.projectlombok:lombok:1.18.36' - annotationProcessor 'org.projectlombok:lombok:1.18.36' - - // Shaded - implementation 'com.dfsek:paralithic:0.8.1' - implementation 'io.papermc:paperlib:1.0.5' - implementation "net.kyori:adventure-text-minimessage:4.17.0" - implementation 'net.kyori:adventure-platform-bukkit:4.3.4' - implementation 'net.kyori:adventure-api:4.17.0' - implementation 'org.bstats:bstats-bukkit:3.1.0' - //implementation 'org.bytedeco:javacpp:1.5.10' - //implementation 'org.bytedeco:cuda-platform:12.3-8.9-1.5.10' - compileOnly 'io.lumine:Mythic-Dist:5.2.1' - compileOnly 'io.lumine:MythicCrucible-Dist:2.0.0' - - // Dynamically Loaded - compileOnly 'io.timeandspace:smoothie-map:2.0.2' - compileOnly 'it.unimi.dsi:fastutil:8.5.8' - compileOnly 'com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2' - compileOnly 'org.zeroturnaround:zt-zip:1.14' - compileOnly 'com.google.code.gson:gson:2.10.1' - compileOnly 'org.ow2.asm:asm:9.2' - compileOnly 'com.google.guava:guava:33.0.0-jre' - compileOnly 'bsf:bsf:2.4.0' - compileOnly 'rhino:js:1.7R2' - compileOnly 'com.github.ben-manes.caffeine:caffeine:3.0.6' - compileOnly 'org.apache.commons:commons-lang3:3.12.0' - compileOnly 'com.github.oshi:oshi-core:6.6.5' - } - - /** - * We need parameter meta for the decree command system - */ - compileJava { - options.compilerArgs << '-parameters' - options.encoding = "UTF-8" - } - - javadoc { - options.encoding = "UTF-8" - options.addStringOption('Xdoclint:none', '-quiet') - } - - task sourcesJar(type: Jar, dependsOn: classes) { - archiveClassifier.set('sources') - from sourceSets.main.allSource - } - - task javadocJar(type: Jar, dependsOn: javadoc) { - archiveClassifier.set('javadoc') - from javadoc.destinationDir - } - - sentry { - includeSourceContext = true - - org = "volmit-software" - projectName = "iris" - authToken = hasProperty("sentry.auth.token") ? - property("sentry.auth.token") : - System.getenv("SENTRY_AUTH_TOKEN") - } -} - -if (JavaVersion.current().toString() != "21") { - System.err.println() - System.err.println("=========================================================================================================") - System.err.println("You must run gradle on Java 21. You are using " + JavaVersion.current()) - System.err.println() - System.err.println("=== For IDEs ===") - System.err.println("1. Configure the project for Java 21") - System.err.println("2. Configure the bundled gradle to use Java 21 in settings") - System.err.println() - System.err.println("=== For Command Line (gradlew) ===") - System.err.println("1. Install JDK 21 from https://www.oracle.com/java/technologies/javase/jdk21-archive-downloads.html") - System.err.println("2. Set JAVA_HOME environment variable to the new jdk installation folder such as C:\\Program Files\\Java\\jdk-21.0.4") - System.err.println("3. Open a new command prompt window to get the new environment variables if need be.") - System.err.println("=========================================================================================================") - System.err.println() - System.exit(69); -} - -task iris(type: Copy) { - group "iris" - from new File(layout.buildDirectory.asFile.get(), "libs/Iris-${version}.jar") - into layout.buildDirectory.asFile.get() - dependsOn(build) -} - -// with classifier: 'javadoc' and 'sources' -task irisDev(type: Copy) { - group "iris" - from("core/build/libs/core-javadoc.jar", "core/build/libs/core-sources.jar") - rename { String fileName -> - fileName.replace("core", "Iris-${version}") - } - into layout.buildDirectory.asFile.get() - dependsOn(iris) - dependsOn("core:sourcesJar") - dependsOn("core:javadocJar") -} - - -def registerCustomOutputTask(name, path) { - if (!System.properties['os.name'].toLowerCase().contains('windows')) { - return; - } - - tasks.register('build' + name, Copy) { - group('development') - outputs.upToDateWhen { false } - dependsOn(iris) - from(new File(buildDir, "Iris-" + version + ".jar")) - into(file(path)) - rename { String fileName -> - fileName.replace("Iris-" + version + ".jar", "Iris.jar") - } - } -} - -def registerCustomOutputTaskUnix(name, path) { - if (System.properties['os.name'].toLowerCase().contains('windows')) { - return; - } - - tasks.register('build' + name, Copy) { - group('development') - outputs.upToDateWhen { false } - dependsOn(iris) - from(new File(buildDir, "Iris-" + version + ".jar")) - into(file(path)) - rename { String fileName -> - fileName.replace("Iris-" + version + ".jar", "Iris.jar") - } - } -} - -tasks.build.dependsOn(shadowJar) - -def cli = file("sentry-cli.exe") -tasks.register("downloadCli", Download) { - group = "sentry" - src "https://release-registry.services.sentry.io/apps/sentry-cli/latest?response=download&arch=x86_64&platform=${System.getProperty("os.name")}&package=sentry-cli" - dest cli - - doLast { - cli.setExecutable(true) - } -} - -tasks.register("release") { - group = "sentry" - dependsOn("downloadCli") - doLast { - def authToken = project.hasProperty("sentry.auth.token") ? - project.property("sentry.auth.token") : - System.getenv("SENTRY_AUTH_TOKEN") - def org = "volmit-software" - def projectName = "iris" - exec { - executable(cli) - args("releases", "new", "--auth-token", authToken, "-o", org, "-p", projectName, version) - } - exec { - executable(cli) - args("releases", "set-commits", "--auth-token", authToken, "-o", org, "-p", projectName, version, "--auto") - } - exec { - executable(cli) - args("releases", "finalize", "--auth-token", authToken, "-o", org, "-p", projectName, version) - } - cli.delete() - } -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 000000000..1ccc60382 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,276 @@ +import com.volmit.nmstools.NMSToolsExtension +import com.volmit.nmstools.NMSToolsPlugin +import de.undercouch.gradle.tasks.download.Download +import xyz.jpenilla.runpaper.task.RunServer +import kotlin.system.exitProcess + +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 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 . + */ + +buildscript { + repositories.maven("https://jitpack.io") + dependencies.classpath("com.github.VolmitSoftware:NMSTools:c5cbc46ce6") +} + +plugins { + java + `java-library` + id("com.gradleup.shadow") version "8.3.6" + id("de.undercouch.download") version "5.0.1" + id("xyz.jpenilla.run-paper") version "2.3.1" + id("io.sentry.jvm.gradle") version "5.7.0" +} + +version = "3.6.10-1.20.1-1.21.5" + +// ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED +// ======================== WINDOWS ============================= +registerCustomOutputTask("Cyberpwn", "C://Users/cyberpwn/Documents/development/server/plugins") +registerCustomOutputTask("Psycho", "C://Dan/MinecraftDevelopment/Server/plugins") +registerCustomOutputTask("ArcaneArts", "C://Users/arcane/Documents/development/server/plugins") +registerCustomOutputTask("Coco", "D://mcsm/plugins") +registerCustomOutputTask("Strange", "D://Servers/1.17 Test Server/plugins") +registerCustomOutputTask("Vatuu", "D://Minecraft/Servers/1.19.4/plugins") +registerCustomOutputTask("CrazyDev22", "C://Users/Julian/Desktop/server/plugins") +registerCustomOutputTask("PixelFury", "C://Users/repix/workplace/Iris/1.21.3 - Development-Public-v3/plugins") +registerCustomOutputTask("PixelFuryDev", "C://Users/repix/workplace/Iris/1.21 - Development-v3/plugins") +// ========================== UNIX ============================== +registerCustomOutputTaskUnix("CyberpwnLT", "/Users/danielmills/development/server/plugins") +registerCustomOutputTaskUnix("PsychoLT", "/Users/brianfopiano/Developer/RemoteGit/Server/plugins") +registerCustomOutputTaskUnix("PixelMac", "/Users/test/Desktop/mcserver/plugins") +registerCustomOutputTaskUnix("CrazyDev22LT", "/home/julian/Desktop/server/plugins") +// ============================================================== + +val serverMinHeap = "2G" +val serverMaxHeap = "8G" +//Valid values are: none, truecolor, indexed256, indexed16, indexed8 +val color = "truecolor" + +val nmsBindings = mapOf( + "v1_21_R4" to "1.21.5-R0.1-SNAPSHOT", + "v1_21_R3" to "1.21.4-R0.1-SNAPSHOT", + "v1_21_R2" to "1.21.3-R0.1-SNAPSHOT", + "v1_21_R1" to "1.21.1-R0.1-SNAPSHOT", + "v1_20_R4" to "1.20.6-R0.1-SNAPSHOT", + "v1_20_R3" to "1.20.4-R0.1-SNAPSHOT", + "v1_20_R2" to "1.20.2-R0.1-SNAPSHOT", + "v1_20_R1" to "1.20.1-R0.1-SNAPSHOT", +) +val jvmVersion = mapOf() +nmsBindings.forEach { key, value -> + project(":nms:$key") { + apply() + apply() + + repositories { + maven("https://libraries.minecraft.net") + } + + extensions.configure(NMSToolsExtension::class) { + jvm = jvmVersion.getOrDefault(key, 21) + version = value + } + + dependencies { + compileOnly(project(":core")) + compileOnly("org.jetbrains:annotations:26.0.2") + } + } + + tasks.register("runServer-$key") { + group = "servers" + minecraftVersion(value.split("-")[0]) + minHeapSize = serverMinHeap + maxHeapSize = serverMaxHeap + pluginJars(tasks.jar.flatMap { it.archiveFile }) + javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(jvmVersion.getOrDefault(key, 21))} + runDirectory.convention(layout.buildDirectory.dir("run/$key")) + systemProperty("disable.watchdog", "") + systemProperty("net.kyori.ansi.colorLevel", color) + systemProperty("com.mojang.eula.agree", true) + } +} + +tasks { + jar { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + nmsBindings.forEach { key, _ -> + from(project(":nms:$key").tasks.named("remap").map { zipTree(it.outputs.files.singleFile) }) + } + from(project(":core").tasks.shadowJar.flatMap { it.archiveFile }.map { zipTree(it) }) + archiveFileName.set("Iris-${project.version}.jar") + } + + register("iris") { + group = "iris" + dependsOn("jar") + from(layout.buildDirectory.file("libs/Iris-${project.version}.jar")) + into(layout.buildDirectory) + } + + register("irisDev") { + group = "iris" + from(project(":core").layout.buildDirectory.files("libs/core-javadoc.jar", "libs/core-sources.jar")) + rename { it.replace("core", "Iris-${project.version}") } + into(layout.buildDirectory) + dependsOn(":core:sourcesJar") + dependsOn(":core:javadocJar") + } + + val cli = file("sentry-cli.exe") + register("downloadCli") { + group = "sentry" + src("https://release-registry.services.sentry.io/apps/sentry-cli/latest?response=download&arch=x86_64&platform=${System.getProperty("os.name")}&package=sentry-cli") + dest(cli) + + doLast { + cli.setExecutable(true) + } + } + + register("release") { + dependsOn("downloadCli") + doLast { + val authToken = project.property("sentry.auth.token") ?: System.getenv("SENTRY_AUTH_TOKEN") + val org = "volmit-software" + val projectName = "iris" + providers.exec { + executable(cli) + args("releases", "new", "--auth-token", authToken, "-o", org, "-p", projectName, version) + }.result.get() + providers.exec { + executable(cli) + args("releases", "set-commits", "--auth-token", authToken, "-o", org, "-p", projectName, version, "--auto") + }.result.get() + providers.exec { + executable(cli) + args("releases", "finalize", "--auth-token", authToken, "-o", org, "-p", projectName, version) + }.result.get() + cli.delete() + } + } +} + +dependencies { + implementation(project(":core")) +} + +configurations.configureEach { + resolutionStrategy.cacheChangingModulesFor(60, "minutes") + resolutionStrategy.cacheDynamicVersionsFor(60, "minutes") +} + +allprojects { + apply() + + repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") + maven("https://repo.codemc.org/repository/maven-public/") + maven("https://mvn.lumine.io/repository/maven-public/") + maven("https://jitpack.io") + + maven("https://s01.oss.sonatype.org/content/repositories/snapshots") + maven("https://mvn.lumine.io/repository/maven/") + maven("https://repo.triumphteam.dev/snapshots") + maven("https://repo.mineinabyss.com/releases") + maven("https://hub.jeff-media.com/nexus/repository/jeff-media-public/") + maven("https://repo.nexomc.com/releases/") + } + + dependencies { + // Provided or Classpath + compileOnly("org.projectlombok:lombok:1.18.36") + annotationProcessor("org.projectlombok:lombok:1.18.36") + } + + /** + * We need parameter meta for the decree command system + */ + tasks { + compileJava { + options.compilerArgs.add("-parameters") + options.encoding = "UTF-8" + } + + javadoc { + options.encoding = "UTF-8" + options.quiet() + //options.addStringOption("Xdoclint:none") // TODO: Re-enable this + } + + register("sourcesJar") { + archiveClassifier.set("sources") + from(sourceSets.main.map { it.allSource }) + } + + register("javadocJar") { + archiveClassifier.set("javadoc") + from(javadoc.map { it.destinationDir!! }) + } + } +} + +if (JavaVersion.current().toString() != "21") { + System.err.println() + System.err.println("=========================================================================================================") + System.err.println("You must run gradle on Java 21. You are using " + JavaVersion.current()) + System.err.println() + System.err.println("=== For IDEs ===") + System.err.println("1. Configure the project for Java 21") + System.err.println("2. Configure the bundled gradle to use Java 21 in settings") + System.err.println() + System.err.println("=== For Command Line (gradlew) ===") + System.err.println("1. Install JDK 21 from https://www.oracle.com/java/technologies/javase/jdk21-archive-downloads.html") + System.err.println("2. Set JAVA_HOME environment variable to the new jdk installation folder such as C:\\Program Files\\Java\\jdk-21.0.4") + System.err.println("3. Open a new command prompt window to get the new environment variables if need be.") + System.err.println("=========================================================================================================") + System.err.println() + exitProcess(69) +} + + +fun registerCustomOutputTask(name: String, path: String) { + if (!System.getProperty("os.name").lowercase().contains("windows")) { + return + } + + tasks.register("build$name") { + group = "development" + outputs.upToDateWhen { false } + dependsOn("iris") + from(layout.buildDirectory.file("Iris-${project.version}.jar")) + into(file(path)) + rename { "Iris.jar" } + } +} + +fun registerCustomOutputTaskUnix(name: String, path: String) { + if (System.getProperty("os.name").lowercase().contains("windows")) { + return + } + + tasks.register("build$name") { + group = "development" + outputs.upToDateWhen { false } + dependsOn("iris") + from(layout.buildDirectory.file("Iris-${project.version}.jar")) + into(file(path)) + rename { "Iris.jar" } + } +} \ No newline at end of file diff --git a/core/build.gradle b/core/build.gradle deleted file mode 100644 index 9effb5654..000000000 --- a/core/build.gradle +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2021 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 . - */ - -plugins { - id 'java' - id 'java-library' - id "io.freefair.lombok" version "8.6" -} - -def apiVersion = '1.19' -def main = 'com.volmit.iris.Iris' - -/** - * We need parameter meta for the decree command system - */ -compileJava { - options.compilerArgs << '-parameters' - options.encoding = "UTF-8" -} - -repositories { - maven { url 'https://nexus.phoenixdevt.fr/repository/maven-public/'} - maven { url 'https://repo.auxilor.io/repository/maven-public/' } -} - -/** - * Dependencies. - * - * Provided or classpath dependencies are not shaded and are available on the runtime classpath - * - * Shaded dependencies are not available at runtime, nor are they available on mvn central so they - * need to be shaded into the jar (increasing binary size) - * - * Dynamically loaded dependencies are defined in the plugin.yml (updating these must be updated in the - * plugin.yml also, otherwise they wont be available). These do not increase binary size). Only declare - * these dependencies if they are available on mvn central. - */ -dependencies { - // Provided or Classpath - compileOnly 'org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT' - compileOnly 'org.apache.logging.log4j:log4j-api:2.19.0' - compileOnly 'org.apache.logging.log4j:log4j-core:2.19.0' - compileOnly 'commons-io:commons-io:2.13.0' - compileOnly 'commons-lang:commons-lang:2.6' - compileOnly 'com.github.oshi:oshi-core:5.8.5' - compileOnly 'org.lz4:lz4-java:1.8.0' - - // Third Party Integrations - compileOnly 'com.ticxo.playeranimator:PlayerAnimator:R1.2.7' - compileOnly 'com.nexomc:nexo:1.6.0' - compileOnly 'com.github.LoneDev6:api-itemsadder:3.4.1-r4' - compileOnly 'com.github.PlaceholderAPI:placeholderapi:2.11.3' - compileOnly 'com.github.Ssomar-Developement:SCore:4.23.10.8' - compileOnly 'net.Indyuce:MMOItems-API:6.9.5-SNAPSHOT' - compileOnly 'com.willfp:EcoItems:5.44.0' - //implementation files('libs/CustomItems.jar') -} - -java { - disableAutoTargetJvm() -} - -/** - * Gradle is weird sometimes, we need to delete the plugin yml from the build folder to actually filter properly. - */ -file(jar.archiveFile.get().getAsFile().getParentFile().getParentFile().getParentFile().getAbsolutePath() + '/build/resources/main/plugin.yml').delete() - -/** - * Expand properties into plugin yml - */ -processResources { - filesMatching('**/plugin.yml') { - expand( - 'name': rootProject.name.toString(), - 'version': rootProject.version.toString(), - 'main': main.toString(), - 'apiversion': apiVersion.toString() - ) - } -} \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts new file mode 100644 index 000000000..a6a1a20c5 --- /dev/null +++ b/core/build.gradle.kts @@ -0,0 +1,151 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 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 . + */ + +plugins { + java + `java-library` + id("com.gradleup.shadow") + id("io.sentry.jvm.gradle") +} + +val apiVersion = "1.19" +val main = "com.volmit.iris.Iris" + +repositories { + maven("https://nexus.phoenixdevt.fr/repository/maven-public/") + maven("https://repo.auxilor.io/repository/maven-public/") +} + +/** + * Dependencies. + * + * Provided or classpath dependencies are not shaded and are available on the runtime classpath + * + * Shaded dependencies are not available at runtime, nor are they available on mvn central so they + * need to be shaded into the jar (increasing binary size) + * + * Dynamically loaded dependencies are defined in the plugin.yml (updating these must be updated in the + * plugin.yml also, otherwise they wont be available). These do not increase binary size). Only declare + * these dependencies if they are available on mvn central. + */ +dependencies { + // Provided or Classpath + compileOnly("org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT") + compileOnly("org.apache.logging.log4j:log4j-api:2.19.0") + compileOnly("org.apache.logging.log4j:log4j-core:2.19.0") + compileOnly("commons-io:commons-io:2.13.0") + compileOnly("commons-lang:commons-lang:2.6") + compileOnly("com.github.oshi:oshi-core:5.8.5") + compileOnly("org.lz4:lz4-java:1.8.0") + + // Third Party Integrations + compileOnly("com.nexomc:nexo:1.6.0") + compileOnly("com.github.LoneDev6:api-itemsadder:3.4.1-r4") + compileOnly("com.github.PlaceholderAPI:placeholderapi:2.11.3") + compileOnly("com.github.Ssomar-Developement:SCore:4.23.10.8") + compileOnly("net.Indyuce:MMOItems-API:6.9.5-SNAPSHOT") + compileOnly("com.willfp:EcoItems:5.44.0") + //implementation files("libs/CustomItems.jar") + + + // Shaded + implementation("com.dfsek:paralithic:0.8.1") + implementation("io.papermc:paperlib:1.0.5") + implementation("net.kyori:adventure-text-minimessage:4.17.0") + implementation("net.kyori:adventure-platform-bukkit:4.3.4") + implementation("net.kyori:adventure-api:4.17.0") + implementation("org.bstats:bstats-bukkit:3.1.0") + + //implementation("org.bytedeco:javacpp:1.5.10") + //implementation("org.bytedeco:cuda-platform:12.3-8.9-1.5.10") + compileOnly("io.lumine:Mythic-Dist:5.2.1") + compileOnly("io.lumine:MythicCrucible-Dist:2.0.0") + + // Dynamically Loaded + compileOnly("io.timeandspace:smoothie-map:2.0.2") + compileOnly("it.unimi.dsi:fastutil:8.5.8") + compileOnly("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2") + compileOnly("org.zeroturnaround:zt-zip:1.14") + compileOnly("com.google.code.gson:gson:2.10.1") + compileOnly("org.ow2.asm:asm:9.2") + compileOnly("com.google.guava:guava:33.0.0-jre") + compileOnly("bsf:bsf:2.4.0") + compileOnly("rhino:js:1.7R2") + compileOnly("com.github.ben-manes.caffeine:caffeine:3.0.6") + compileOnly("org.apache.commons:commons-lang3:3.12.0") + compileOnly("com.github.oshi:oshi-core:6.6.5") +} + +java { + disableAutoTargetJvm() +} + +sentry { + includeSourceContext = true + + org = "volmit-software" + projectName = "iris" + authToken = property("sentry.auth.token") as String? ?: System.getenv("SENTRY_AUTH_TOKEN") +} + +tasks { + /** + * We need parameter meta for the decree command system + */ + compileJava { + options.compilerArgs.add("-parameters") + options.encoding = "UTF-8" + } + + /** + * Expand properties into plugin yml + */ + processResources { + inputs.properties( + "name" to rootProject.name, + "version" to rootProject.version, + "apiVersion" to apiVersion, + "main" to main + ) + filesMatching("**/plugin.yml") { + expand(inputs.properties) + } + } + + shadowJar { + mergeServiceFiles() + relocate("com.dfsek.paralithic", "com.volmit.iris.util.paralithic") + relocate("io.papermc.lib", "com.volmit.iris.util.paper") + relocate("net.kyori", "com.volmit.iris.util.kyori") + relocate("org.bstats", "com.volmit.iris.util.metrics") + relocate("io.sentry", "com.volmit.iris.util.sentry") + + //minimize() + dependencies { + exclude(dependency("org.ow2.asm:asm:")) + exclude(dependency("org.jetbrains:")) + } + } +} + +/** + * Gradle is weird sometimes, we need to delete the plugin yml from the build folder to actually filter properly. + */ +afterEvaluate { + layout.buildDirectory.file("resources/main/plugin.yml").get().asFile.delete() +} \ No newline at end of file diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml index 7ff73ff7e..0a58f5d00 100644 --- a/core/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -23,5 +23,5 @@ libraries: commands: iris: aliases: [ ir, irs ] -api-version: '${apiversion}' +api-version: '${apiVersion}' hotload-dependencies: false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ zQccdaaVK$5bz;*rnyK{_SQYM>;aES6Qs^lj9lEs6_J+%nIiuQC*fN;z8md>r_~Mfl zU%p5Dt_YT>gQqfr@`cR!$NWr~+`CZb%dn;WtzrAOI>P_JtsB76PYe*<%H(y>qx-`Kq!X_; z<{RpAqYhE=L1r*M)gNF3B8r(<%8mo*SR2hu zccLRZwGARt)Hlo1euqTyM>^!HK*!Q2P;4UYrysje@;(<|$&%vQekbn|0Ruu_Io(w4#%p6ld2Yp7tlA`Y$cciThP zKzNGIMPXX%&Ud0uQh!uQZz|FB`4KGD?3!ND?wQt6!n*f4EmCoJUh&b?;B{|lxs#F- z31~HQ`SF4x$&v00@(P+j1pAaj5!s`)b2RDBp*PB=2IB>oBF!*6vwr7Dp%zpAx*dPr zb@Zjq^XjN?O4QcZ*O+8>)|HlrR>oD*?WQl5ri3R#2?*W6iJ>>kH%KnnME&TT@ZzrHS$Q%LC?n|e>V+D+8D zYc4)QddFz7I8#}y#Wj6>4P%34dZH~OUDb?uP%-E zwjXM(?Sg~1!|wI(RVuxbu)-rH+O=igSho_pDCw(c6b=P zKk4ATlB?bj9+HHlh<_!&z0rx13K3ZrAR8W)!@Y}o`?a*JJsD+twZIv`W)@Y?Amu_u zz``@-e2X}27$i(2=9rvIu5uTUOVhzwu%mNazS|lZb&PT;XE2|B&W1>=B58#*!~D&) zfVmJGg8UdP*fx(>Cj^?yS^zH#o-$Q-*$SnK(ZVFkw+er=>N^7!)FtP3y~Xxnu^nzY zikgB>Nj0%;WOltWIob|}%lo?_C7<``a5hEkx&1ku$|)i>Rh6@3h*`slY=9U}(Ql_< zaNG*J8vb&@zpdhAvv`?{=zDedJ23TD&Zg__snRAH4eh~^oawdYi6A3w8<Ozh@Kw)#bdktM^GVb zrG08?0bG?|NG+w^&JvD*7LAbjED{_Zkc`3H!My>0u5Q}m!+6VokMLXxl`Mkd=g&Xx z-a>m*#G3SLlhbKB!)tnzfWOBV;u;ftU}S!NdD5+YtOjLg?X}dl>7m^gOpihrf1;PY zvll&>dIuUGs{Qnd- zwIR3oIrct8Va^Tm0t#(bJD7c$Z7DO9*7NnRZorrSm`b`cxz>OIC;jSE3DO8`hX955ui`s%||YQtt2 z5DNA&pG-V+4oI2s*x^>-$6J?p=I>C|9wZF8z;VjR??Icg?1w2v5Me+FgAeGGa8(3S z4vg*$>zC-WIVZtJ7}o9{D-7d>zCe|z#<9>CFve-OPAYsneTb^JH!Enaza#j}^mXy1 z+ULn^10+rWLF6j2>Ya@@Kq?26>AqK{A_| zQKb*~F1>sE*=d?A?W7N2j?L09_7n+HGi{VY;MoTGr_)G9)ot$p!-UY5zZ2Xtbm=t z@dpPSGwgH=QtIcEulQNI>S-#ifbnO5EWkI;$A|pxJd885oM+ zGZ0_0gDvG8q2xebj+fbCHYfAXuZStH2j~|d^sBAzo46(K8n59+T6rzBwK)^rfPT+B zyIFw)9YC-V^rhtK`!3jrhmW-sTmM+tPH+;nwjL#-SjQPUZ53L@A>y*rt(#M(qsiB2 zx6B)dI}6Wlsw%bJ8h|(lhkJVogQZA&n{?Vgs6gNSXzuZpEyu*xySy8ro07QZ7Vk1!3tJphN_5V7qOiyK8p z#@jcDD8nmtYi1^l8ml;AF<#IPK?!pqf9D4moYk>d99Im}Jtwj6c#+A;f)CQ*f-hZ< z=p_T86jog%!p)D&5g9taSwYi&eP z#JuEK%+NULWus;0w32-SYFku#i}d~+{Pkho&^{;RxzP&0!RCm3-9K6`>KZpnzS6?L z^H^V*s!8<>x8bomvD%rh>Zp3>Db%kyin;qtl+jAv8Oo~1g~mqGAC&Qi_wy|xEt2iz zWAJEfTV%cl2Cs<1L&DLRVVH05EDq`pH7Oh7sR`NNkL%wi}8n>IXcO40hp+J+sC!W?!krJf!GJNE8uj zg-y~Ns-<~D?yqbzVRB}G>0A^f0!^N7l=$m0OdZuqAOQqLc zX?AEGr1Ht+inZ-Qiwnl@Z0qukd__a!C*CKuGdy5#nD7VUBM^6OCpxCa2A(X;e0&V4 zM&WR8+wErQ7UIc6LY~Q9x%Sn*Tn>>P`^t&idaOEnOd(Ufw#>NoR^1QdhJ8s`h^|R_ zXX`c5*O~Xdvh%q;7L!_!ohf$NfEBmCde|#uVZvEo>OfEq%+Ns7&_f$OR9xsihRpBb z+cjk8LyDm@U{YN>+r46?nn{7Gh(;WhFw6GAxtcKD+YWV?uge>;+q#Xx4!GpRkVZYu zzsF}1)7$?%s9g9CH=Zs+B%M_)+~*j3L0&Q9u7!|+T`^O{xE6qvAP?XWv9_MrZKdo& z%IyU)$Q95AB4!#hT!_dA>4e@zjOBD*Y=XjtMm)V|+IXzjuM;(l+8aA5#Kaz_$rR6! zj>#&^DidYD$nUY(D$mH`9eb|dtV0b{S>H6FBfq>t5`;OxA4Nn{J(+XihF(stSche7$es&~N$epi&PDM_N`As;*9D^L==2Q7Z2zD+CiU(|+-kL*VG+&9!Yb3LgPy?A zm7Z&^qRG_JIxK7-FBzZI3Q<;{`DIxtc48k> zc|0dmX;Z=W$+)qE)~`yn6MdoJ4co;%!`ddy+FV538Y)j(vg}5*k(WK)KWZ3WaOG!8 z!syGn=s{H$odtpqFrT#JGM*utN7B((abXnpDM6w56nhw}OY}0TiTG1#f*VFZr+^-g zbP10`$LPq_;PvrA1XXlyx2uM^mrjTzX}w{yuLo-cOClE8MMk47T25G8M!9Z5ypOSV zAJUBGEg5L2fY)ZGJb^E34R2zJ?}Vf>{~gB!8=5Z) z9y$>5c)=;o0HeHHSuE4U)#vG&KF|I%-cF6f$~pdYJWk_dD}iOA>iA$O$+4%@>JU08 zS`ep)$XLPJ+n0_i@PkF#ri6T8?ZeAot$6JIYHm&P6EB=BiaNY|aA$W0I+nz*zkz_z zkEru!tj!QUffq%)8y0y`T&`fuus-1p>=^hnBiBqD^hXrPs`PY9tU3m0np~rISY09> z`P3s=-kt_cYcxWd{de@}TwSqg*xVhp;E9zCsnXo6z z?f&Sv^U7n4`xr=mXle94HzOdN!2kB~4=%)u&N!+2;z6UYKUDqi-s6AZ!haB;@&B`? z_TRX0%@suz^TRdCb?!vNJYPY8L_}&07uySH9%W^Tc&1pia6y1q#?*Drf}GjGbPjBS zbOPcUY#*$3sL2x4v_i*Y=N7E$mR}J%|GUI(>WEr+28+V z%v5{#e!UF*6~G&%;l*q*$V?&r$Pp^sE^i-0$+RH3ERUUdQ0>rAq2(2QAbG}$y{de( z>{qD~GGuOk559Y@%$?N^1ApVL_a704>8OD%8Y%8B;FCt%AoPu8*D1 zLB5X>b}Syz81pn;xnB}%0FnwazlWfUV)Z-~rZg6~b z6!9J$EcE&sEbzcy?CI~=boWA&eeIa%z(7SE^qgVLz??1Vbc1*aRvc%Mri)AJaAG!p z$X!_9Ds;Zz)f+;%s&dRcJt2==P{^j3bf0M=nJd&xwUGlUFn?H=2W(*2I2Gdu zv!gYCwM10aeus)`RIZSrCK=&oKaO_Ry~D1B5!y0R=%!i2*KfXGYX&gNv_u+n9wiR5 z*e$Zjju&ODRW3phN925%S(jL+bCHv6rZtc?!*`1TyYXT6%Ju=|X;6D@lq$8T zW{Y|e39ioPez(pBH%k)HzFITXHvnD6hw^lIoUMA;qAJ^CU?top1fo@s7xT13Fvn1H z6JWa-6+FJF#x>~+A;D~;VDs26>^oH0EI`IYT2iagy23?nyJ==i{g4%HrAf1-*v zK1)~@&(KkwR7TL}L(A@C_S0G;-GMDy=MJn2$FP5s<%wC)4jC5PXoxrQBFZ_k0P{{s@sz+gX`-!=T8rcB(=7vW}^K6oLWMmp(rwDh}b zwaGGd>yEy6fHv%jM$yJXo5oMAQ>c9j`**}F?MCry;T@47@r?&sKHgVe$MCqk#Z_3S z1GZI~nOEN*P~+UaFGnj{{Jo@16`(qVNtbU>O0Hf57-P>x8Jikp=`s8xWs^dAJ9lCQ z)GFm+=OV%AMVqVATtN@|vp61VVAHRn87}%PC^RAzJ%JngmZTasWBAWsoAqBU+8L8u z4A&Pe?fmTm0?mK-BL9t+{y7o(7jm+RpOhL9KnY#E&qu^}B6=K_dB}*VlSEiC9fn)+V=J;OnN)Ta5v66ic1rG+dGAJ1 z1%Zb_+!$=tQ~lxQrzv3x#CPb?CekEkA}0MYSgx$Jdd}q8+R=ma$|&1a#)TQ=l$1tQ z=tL9&_^vJ)Pk}EDO-va`UCT1m#Uty1{v^A3P~83_#v^ozH}6*9mIjIr;t3Uv%@VeW zGL6(CwCUp)Jq%G0bIG%?{_*Y#5IHf*5M@wPo6A{$Um++Co$wLC=J1aoG93&T7Ho}P z=mGEPP7GbvoG!uD$k(H3A$Z))+i{Hy?QHdk>3xSBXR0j!11O^mEe9RHmw!pvzv?Ua~2_l2Yh~_!s1qS`|0~0)YsbHSz8!mG)WiJE| z2f($6TQtt6L_f~ApQYQKSb=`053LgrQq7G@98#igV>y#i==-nEjQ!XNu9 z~;mE+gtj4IDDNQJ~JVk5Ux6&LCSFL!y=>79kE9=V}J7tD==Ga+IW zX)r7>VZ9dY=V&}DR))xUoV!u(Z|%3ciQi_2jl}3=$Agc(`RPb z8kEBpvY>1FGQ9W$n>Cq=DIpski};nE)`p3IUw1Oz0|wxll^)4dq3;CCY@RyJgFgc# zKouFh!`?Xuo{IMz^xi-h=StCis_M7yq$u) z?XHvw*HP0VgR+KR6wI)jEMX|ssqYvSf*_3W8zVTQzD?3>H!#>InzpSO)@SC8q*ii- z%%h}_#0{4JG;Jm`4zg};BPTGkYamx$Xo#O~lBirRY)q=5M45n{GCfV7h9qwyu1NxOMoP4)jjZMxmT|IQQh0U7C$EbnMN<3)Kk?fFHYq$d|ICu>KbY_hO zTZM+uKHe(cIZfEqyzyYSUBZa8;Fcut-GN!HSA9ius`ltNebF46ZX_BbZNU}}ZOm{M2&nANL9@0qvih15(|`S~z}m&h!u4x~(%MAO$jHRWNfuxWF#B)E&g3ghSQ9|> z(MFaLQj)NE0lowyjvg8z0#m6FIuKE9lDO~Glg}nSb7`~^&#(Lw{}GVOS>U)m8bF}x zVjbXljBm34Cs-yM6TVusr+3kYFjr28STT3g056y3cH5Tmge~ASxBj z%|yb>$eF;WgrcOZf569sDZOVwoo%8>XO>XQOX1OyN9I-SQgrm;U;+#3OI(zrWyow3 zk==|{lt2xrQ%FIXOTejR>;wv(Pb8u8}BUpx?yd(Abh6? zsoO3VYWkeLnF43&@*#MQ9-i-d0t*xN-UEyNKeyNMHw|A(k(_6QKO=nKMCxD(W(Yop zsRQ)QeL4X3Lxp^L%wzi2-WVSsf61dqliPUM7srDB?Wm6Lzn0&{*}|IsKQW;02(Y&| zaTKv|`U(pSzuvR6Rduu$wzK_W-Y-7>7s?G$)U}&uK;<>vU}^^ns@Z!p+9?St1s)dG zK%y6xkPyyS1$~&6v{kl?Md6gwM|>mt6Upm>oa8RLD^8T{0?HC!Z>;(Bob7el(DV6x zi`I)$&E&ngwFS@bi4^xFLAn`=fzTC;aimE^!cMI2n@Vo%Ae-ne`RF((&5y6xsjjAZ zVguVoQ?Z9uk$2ON;ersE%PU*xGO@T*;j1BO5#TuZKEf(mB7|g7pcEA=nYJ{s3vlbg zd4-DUlD{*6o%Gc^N!Nptgay>j6E5;3psI+C3Q!1ZIbeCubW%w4pq9)MSDyB{HLm|k zxv-{$$A*pS@csolri$Ge<4VZ}e~78JOL-EVyrbxKra^d{?|NnPp86!q>t<&IP07?Z z^>~IK^k#OEKgRH+LjllZXk7iA>2cfH6+(e&9ku5poo~6y{GC5>(bRK7hwjiurqAiZ zg*DmtgY}v83IjE&AbiWgMyFbaRUPZ{lYiz$U^&Zt2YjG<%m((&_JUbZcfJ22(>bi5 z!J?<7AySj0JZ&<-qXX;mcV!f~>G=sB0KnjWca4}vrtunD^1TrpfeS^4dvFr!65knK zZh`d;*VOkPs4*-9kL>$GP0`(M!j~B;#x?Ba~&s6CopvO86oM?-? zOw#dIRc;6A6T?B`Qp%^<U5 z19x(ywSH$_N+Io!6;e?`tWaM$`=Db!gzx|lQ${DG!zb1Zl&|{kX0y6xvO1o z220r<-oaS^^R2pEyY;=Qllqpmue|5yI~D|iI!IGt@iod{Opz@*ml^w2bNs)p`M(Io z|E;;m*Xpjd9l)4G#KaWfV(t8YUn@A;nK^#xgv=LtnArX|vWQVuw3}B${h+frU2>9^ z!l6)!Uo4`5k`<<;E(ido7M6lKTgWezNLq>U*=uz&s=cc$1%>VrAeOoUtA|T6gO4>UNqsdK=NF*8|~*sl&wI=x9-EGiq*aqV!(VVXA57 zw9*o6Ir8Lj1npUXvlevtn(_+^X5rzdR>#(}4YcB9O50q97%rW2me5_L=%ffYPUSRc z!vv?Kv>dH994Qi>U(a<0KF6NH5b16enCp+mw^Hb3Xs1^tThFpz!3QuN#}KBbww`(h z7GO)1olDqy6?T$()R7y%NYx*B0k_2IBiZ14&8|JPFxeMF{vSTxF-Vi3+ZOI=Thq2} zyQgjYY1_7^ZQHh{?P))4+qUiQJLi1&{yE>h?~jU%tjdV0h|FENbM3X(KnJdPKc?~k zh=^Ixv*+smUll!DTWH!jrV*wSh*(mx0o6}1@JExzF(#9FXgmTXVoU+>kDe68N)dkQ zH#_98Zv$}lQwjKL@yBd;U(UD0UCl322=pav<=6g>03{O_3oKTq;9bLFX1ia*lw;#K zOiYDcBJf)82->83N_Y(J7Kr_3lE)hAu;)Q(nUVydv+l+nQ$?|%MWTy`t>{havFSQloHwiIkGK9YZ79^9?AZo0ZyQlVR#}lF%dn5n%xYksXf8gnBm=wO7g_^! zauQ-bH1Dc@3ItZ-9D_*pH}p!IG7j8A_o94#~>$LR|TFq zZ-b00*nuw|-5C2lJDCw&8p5N~Z1J&TrcyErds&!l3$eSz%`(*izc;-?HAFD9AHb-| z>)id`QCrzRws^9(#&=pIx9OEf2rmlob8sK&xPCWS+nD~qzU|qG6KwA{zbikcfQrdH z+ zQg>O<`K4L8rN7`GJB0*3<3`z({lWe#K!4AZLsI{%z#ja^OpfjU{!{)x0ZH~RB0W5X zTwN^w=|nA!4PEU2=LR05x~}|B&ZP?#pNgDMwD*ajI6oJqv!L81gu=KpqH22avXf0w zX3HjbCI!n9>l046)5rr5&v5ja!xkKK42zmqHzPx$9Nn_MZk`gLeSLgC=LFf;H1O#B zn=8|^1iRrujHfbgA+8i<9jaXc;CQBAmQvMGQPhFec2H1knCK2x!T`e6soyrqCamX% zTQ4dX_E*8so)E*TB$*io{$c6X)~{aWfaqdTh=xEeGvOAN9H&-t5tEE-qso<+C!2>+ zskX51H-H}#X{A75wqFe-J{?o8Bx|>fTBtl&tcbdR|132Ztqu5X0i-pisB-z8n71%q%>EF}yy5?z=Ve`}hVh{Drv1YWL zW=%ug_&chF11gDv3D6B)Tz5g54H0mDHNjuKZ+)CKFk4Z|$RD zfRuKLW`1B>B?*RUfVd0+u8h3r-{@fZ{k)c!93t1b0+Q9vOaRnEn1*IL>5Z4E4dZ!7 ztp4GP-^1d>8~LMeb}bW!(aAnB1tM_*la=Xx)q(I0Y@__Zd$!KYb8T2VBRw%e$iSdZ zkwdMwd}eV9q*;YvrBFTv1>1+}{H!JK2M*C|TNe$ZSA>UHKk);wz$(F$rXVc|sI^lD zV^?_J!3cLM;GJuBMbftbaRUs$;F}HDEDtIeHQ)^EJJ1F9FKJTGH<(Jj`phE6OuvE) zqK^K`;3S{Y#1M@8yRQwH`?kHMq4tHX#rJ>5lY3DM#o@or4&^_xtBC(|JpGTfrbGkA z2Tu+AyT^pHannww!4^!$5?@5v`LYy~T`qs7SYt$JgrY(w%C+IWA;ZkwEF)u5sDvOK zGk;G>Mh&elvXDcV69J_h02l&O;!{$({fng9Rlc3ID#tmB^FIG^w{HLUpF+iB`|
NnX)EH+Nua)3Y(c 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 48c0a02ca..ff23a68d7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c78733..1aa94a426 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +80,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +131,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -205,6 +214,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f93..7101f8e46 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +41,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/settings.gradle b/settings.gradle.kts similarity index 74% rename from settings.gradle rename to settings.gradle.kts index 9315a1131..c45462895 100644 --- a/settings.gradle +++ b/settings.gradle.kts @@ -23,19 +23,19 @@ pluginManagement { } } plugins { - id "org.gradle.toolchains.foojay-resolver-convention" version "0.8.0" + id ("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } -rootProject.name = 'Iris' +rootProject.name = "Iris" -include(':core') +include(":core") include( - ':nms:v1_21_R4', - ':nms:v1_21_R3', - ':nms:v1_21_R2', - ':nms:v1_21_R1', - ':nms:v1_20_R4', - ':nms:v1_20_R3', - ':nms:v1_20_R2', - ':nms:v1_20_R1', + ":nms:v1_21_R4", + ":nms:v1_21_R3", + ":nms:v1_21_R2", + ":nms:v1_21_R1", + ":nms:v1_20_R4", + ":nms:v1_20_R3", + ":nms:v1_20_R2", + ":nms:v1_20_R1", ) \ No newline at end of file From c0d17742e806b715cf4d97da8b3995583f0738e0 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 13:43:00 +0200 Subject: [PATCH 064/138] fix release task for sentry --- build.gradle.kts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 1ccc60382..24613a9f5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -134,7 +134,7 @@ tasks { val cli = file("sentry-cli.exe") register("downloadCli") { - group = "sentry" + group = "io.sentry" src("https://release-registry.services.sentry.io/apps/sentry-cli/latest?response=download&arch=x86_64&platform=${System.getProperty("os.name")}&package=sentry-cli") dest(cli) @@ -144,28 +144,28 @@ tasks { } register("release") { + group = "io.sentry" dependsOn("downloadCli") doLast { val authToken = project.property("sentry.auth.token") ?: System.getenv("SENTRY_AUTH_TOKEN") val org = "volmit-software" val projectName = "iris" - providers.exec { - executable(cli) - args("releases", "new", "--auth-token", authToken, "-o", org, "-p", projectName, version) - }.result.get() - providers.exec { - executable(cli) - args("releases", "set-commits", "--auth-token", authToken, "-o", org, "-p", projectName, version, "--auto") - }.result.get() - providers.exec { - executable(cli) - args("releases", "finalize", "--auth-token", authToken, "-o", org, "-p", projectName, version) - }.result.get() + exec(cli, "releases", "new", "--auth-token", authToken, "-o", org, "-p", projectName, version) + exec(cli, "releases", "set-commits", "--auth-token", authToken, "-o", org, "-p", projectName, version, "--auto", "--ignore-missing") + exec(cli, "releases", "finalize", "--auth-token", authToken, "-o", org, "-p", projectName, version) cli.delete() } } } +fun exec(vararg command: Any) { + val p = ProcessBuilder(command.map { it.toString() }) + .start() + p.inputStream.reader().useLines { it.forEach(::println) } + p.errorStream.reader().useLines { it.forEach(::println) } + p.waitFor() +} + dependencies { implementation(project(":core")) } From b8ee7561ddcb0e8d044f63e81a16ff9abad82c65 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 13:44:57 +0200 Subject: [PATCH 065/138] fix failing to create temp file when user deleted the temp directory --- .../java/com/volmit/iris/util/mantle/Mantle.java | 1 - .../com/volmit/iris/util/mantle/TectonicPlate.java | 13 +++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java index 9de145a3f..6ec24717d 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -85,7 +85,6 @@ public class Mantle { this.worldHeight = worldHeight; this.ioTrim = new AtomicBoolean(false); this.ioTectonicUnload = new AtomicBoolean(false); - new File(dataFolder, ".tmp").mkdirs(); loadedRegions = new KMap<>(); lastUse = new KMap<>(); ioBurst = MultiBurst.burst; diff --git a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java index 009993321..101019023 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java @@ -27,6 +27,7 @@ import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; import com.volmit.iris.util.io.CountingDataInputStream; +import com.volmit.iris.util.io.IO; import com.volmit.iris.util.scheduling.PrecisionStopwatch; import lombok.Getter; import net.jpountz.lz4.LZ4BlockInputStream; @@ -222,16 +223,8 @@ public class TectonicPlate { */ public void write(File file) throws IOException { PrecisionStopwatch p = PrecisionStopwatch.start(); - File temp = File.createTempFile("iris-tectonic-plate", ".bin", new File(file.getParentFile(), ".tmp")); - try { - try (DataOutputStream dos = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(temp)))) { - write(dos); - } - Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); - Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); - } finally { - temp.delete(); - } + IO.write(file, out -> new DataOutputStream(new LZ4BlockOutputStream(out)), this::write); + Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); } /** From 9cf567e1ff75e880b342a88674d894d100a9977c Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 13:47:24 +0200 Subject: [PATCH 066/138] remove unused util to fix class not found exception --- .../iris/util/particle/FastParticle.java | 179 ---------------- .../iris/util/particle/FastReflection.java | 79 ------- .../iris/util/particle/ParticleSender.java | 124 ----------- .../util/particle/ParticleSenderLegacy.java | 185 ----------------- .../iris/util/particle/ParticleType.java | 192 ------------------ 5 files changed, 759 deletions(-) delete mode 100644 core/src/main/java/com/volmit/iris/util/particle/FastParticle.java delete mode 100644 core/src/main/java/com/volmit/iris/util/particle/FastReflection.java delete mode 100644 core/src/main/java/com/volmit/iris/util/particle/ParticleSender.java delete mode 100644 core/src/main/java/com/volmit/iris/util/particle/ParticleSenderLegacy.java delete mode 100644 core/src/main/java/com/volmit/iris/util/particle/ParticleType.java diff --git a/core/src/main/java/com/volmit/iris/util/particle/FastParticle.java b/core/src/main/java/com/volmit/iris/util/particle/FastParticle.java deleted file mode 100644 index 4052dbdd2..000000000 --- a/core/src/main/java/com/volmit/iris/util/particle/FastParticle.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * 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 com.volmit.iris.util.particle; - -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.entity.Player; - -/** - * Simple Bukkit Particles API with 1.7 to 1.13.2 support ! - *

- * You can find the project on GitHub - * - * @author MrMicky - */ -public final class FastParticle { - - private static final ParticleSender PARTICLE_SENDER; - - static { - if (FastReflection.optionalClass("org.bukkit.Particle$DustOptions").isPresent()) { - PARTICLE_SENDER = new ParticleSender.ParticleSender1_13(); - } else if (FastReflection.optionalClass("org.bukkit.Particle").isPresent()) { - PARTICLE_SENDER = new ParticleSender.ParticleSenderImpl(); - } else { - PARTICLE_SENDER = new ParticleSenderLegacy(); - } - } - - private FastParticle() { - throw new UnsupportedOperationException(); - } - - /* - * Worlds methods - */ - public static void spawnParticle(World world, ParticleType particle, Location location, int count) { - spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count); - } - - public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count) { - spawnParticle(world, particle, x, y, z, count, null); - } - - public static void spawnParticle(World world, ParticleType particle, Location location, int count, T data) { - spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, data); - } - - public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count, - T data) { - spawnParticle(world, particle, x, y, z, count, 0.0D, 0.0D, 0.0D, data); - } - - public static void spawnParticle(World world, ParticleType particle, Location location, int count, double offsetX, - double offsetY, double offsetZ) { - spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ); - } - - public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ) { - spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, null); - } - - public static void spawnParticle(World world, ParticleType particle, Location location, int count, - double offsetX, double offsetY, double offsetZ, T data) { - spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, - offsetZ, data); - } - - public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, T data) { - spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, 1.0D, data); - } - - public static void spawnParticle(World world, ParticleType particle, Location location, int count, double offsetX, - double offsetY, double offsetZ, double extra) { - spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra); - } - - public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, double extra) { - spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null); - } - - public static void spawnParticle(World world, ParticleType particle, Location location, int count, - double offsetX, double offsetY, double offsetZ, double extra, T data) { - spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data); - } - - public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, double extra, T data) { - sendParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); - } - - /* - * Player methods - */ - public static void spawnParticle(Player player, ParticleType particle, Location location, int count) { - spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count); - } - - public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count) { - spawnParticle(player, particle, x, y, z, count, null); - } - - public static void spawnParticle(Player player, ParticleType particle, Location location, int count, T data) { - spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, data); - } - - public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count, - T data) { - spawnParticle(player, particle, x, y, z, count, 0.0D, 0.0D, 0.0D, data); - } - - public static void spawnParticle(Player player, ParticleType particle, Location location, int count, double offsetX, - double offsetY, double offsetZ) { - spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ); - } - - public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ) { - spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, null); - } - - public static void spawnParticle(Player player, ParticleType particle, Location location, int count, - double offsetX, double offsetY, double offsetZ, T data) { - spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, data); - } - - public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, T data) { - spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, 1.0D, data); - } - - public static void spawnParticle(Player player, ParticleType particle, Location location, int count, double offsetX, - double offsetY, double offsetZ, double extra) { - spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra); - } - - public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, double extra) { - spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null); - } - - public static void spawnParticle(Player player, ParticleType particle, Location location, int count, - double offsetX, double offsetY, double offsetZ, double extra, T data) { - spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data); - } - - public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, double extra, T data) { - sendParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); - } - - private static void sendParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, double extra, Object data) { - if (!particle.isSupported()) { - throw new IllegalArgumentException("The particle '" + particle + "' is not compatible with your server version"); - } - - PARTICLE_SENDER.spawnParticle(receiver, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); - } -} diff --git a/core/src/main/java/com/volmit/iris/util/particle/FastReflection.java b/core/src/main/java/com/volmit/iris/util/particle/FastReflection.java deleted file mode 100644 index ee5994fb6..000000000 --- a/core/src/main/java/com/volmit/iris/util/particle/FastReflection.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 com.volmit.iris.util.particle; - -import com.volmit.iris.Iris; -import org.bukkit.Bukkit; - -import java.util.Optional; - -/** - * Small reflection class to use CraftBukkit and NMS - * - * @author MrMicky - */ -public final class FastReflection { - - public static final String OBC_PACKAGE = "org.bukkit.craftbukkit"; - public static final String NMS_PACKAGE = "net.minecraft.server"; - - public static final String VERSION = Bukkit.getServer().getClass().getPackage().getName().substring(OBC_PACKAGE.length() + 1); - - private FastReflection() { - throw new UnsupportedOperationException(); - } - - public static String nmsClassName(String className) { - return NMS_PACKAGE + '.' + VERSION + '.' + className; - } - - public static Class nmsClass(String className) throws ClassNotFoundException { - return Class.forName(nmsClassName(className)); - } - - public static Optional> nmsOptionalClass(String className) { - return optionalClass(nmsClassName(className)); - } - - public static String obcClassName(String className) { - return OBC_PACKAGE + '.' + VERSION + '.' + className; - } - - public static Class obcClass(String className) throws ClassNotFoundException { - return Class.forName(obcClassName(className)); - } - - public static Optional> obcOptionalClass(String className) { - return optionalClass(obcClassName(className)); - } - - public static Optional> optionalClass(String className) { - try { - return Optional.of(Class.forName(className)); - } catch (ClassNotFoundException e) { - Iris.reportError(e); - return Optional.empty(); - } - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - public static Object enumValueOf(Class enumClass, String enumName) { - return Enum.valueOf((Class) enumClass, enumName.toUpperCase()); - } -} \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/util/particle/ParticleSender.java b/core/src/main/java/com/volmit/iris/util/particle/ParticleSender.java deleted file mode 100644 index d9ac0a401..000000000 --- a/core/src/main/java/com/volmit/iris/util/particle/ParticleSender.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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 com.volmit.iris.util.particle; - -import com.volmit.iris.Iris; -import org.bukkit.Bukkit; -import org.bukkit.Color; -import org.bukkit.Particle; -import org.bukkit.World; -import org.bukkit.block.data.BlockData; -import org.bukkit.entity.Player; -import org.bukkit.material.MaterialData; - -/** - * Particle sender using the Bukkit particles API for 1.9+ servers - * - * @author MrMicky - */ -@SuppressWarnings("deprecation") -interface ParticleSender { - - void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data); - - Object getParticle(ParticleType particle); - - boolean isValidData(Object particle, Object data); - - default double color(double color) { - return color / 255.0; - } - - class ParticleSenderImpl implements ParticleSender { - - @Override - public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data) { - Particle bukkitParticle = Particle.valueOf(particle.toString()); - - if (data instanceof Color) { - if (particle.getDataType() == Color.class) { - Color color = (Color) data; - count = 0; - offsetX = color(color.getRed()); - offsetY = color(color.getGreen()); - offsetZ = color(color.getBlue()); - extra = 1.0; - } - data = null; - } - - if (receiver instanceof World) { - ((World) receiver).spawnParticle(bukkitParticle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); - } else if (receiver instanceof Player) { - ((Player) receiver).spawnParticle(bukkitParticle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); - } - } - - @Override - public Particle getParticle(ParticleType particle) { - try { - return Particle.valueOf(particle.toString()); - } catch (IllegalArgumentException e) { - Iris.reportError(e); - return null; - } - } - - @Override - public boolean isValidData(Object particle, Object data) { - return isValidDataBukkit((Particle) particle, data); - } - - public boolean isValidDataBukkit(Particle particle, Object data) { - return particle.getDataType() == Void.class || particle.getDataType().isInstance(data); - } - } - - class ParticleSender1_13 extends ParticleSenderImpl { - @Override - public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data) { - Particle bukkitParticle = Particle.valueOf(particle.toString()); - - if (bukkitParticle.getDataType() == Particle.DustOptions.class) { - if (data instanceof Color) { - data = new Particle.DustOptions((Color) data, 1); - } else if (data == null) { - data = new Particle.DustOptions(Color.RED, 1); - } - } else if (bukkitParticle.getDataType() == BlockData.class && data instanceof MaterialData) { - data = Bukkit.createBlockData(((MaterialData) data).getItemType()); - } - - super.spawnParticle(receiver, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); - } - - @Override - public boolean isValidDataBukkit(Particle particle, Object data) { - if (particle.getDataType() == Particle.DustOptions.class && data instanceof Color) { - return true; - } - - if (particle.getDataType() == BlockData.class && data instanceof MaterialData) { - return true; - } - - return super.isValidDataBukkit(particle, data); - } - } -} diff --git a/core/src/main/java/com/volmit/iris/util/particle/ParticleSenderLegacy.java b/core/src/main/java/com/volmit/iris/util/particle/ParticleSenderLegacy.java deleted file mode 100644 index 1668de313..000000000 --- a/core/src/main/java/com/volmit/iris/util/particle/ParticleSenderLegacy.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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 com.volmit.iris.util.particle; - -import com.volmit.iris.Iris; -import org.bukkit.Color; -import org.bukkit.World; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.material.MaterialData; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -/** - * Legacy particle sender with NMS for 1.7/1.8 servers - * - * @author MrMicky - */ -@SuppressWarnings({"deprecation", "JavaReflectionInvocation"}) -class ParticleSenderLegacy implements ParticleSender { - - private static final boolean SERVER_IS_1_8; - - private static final Constructor PACKET_PARTICLE; - private static final Class ENUM_PARTICLE; - - private static final Method WORLD_GET_HANDLE; - private static final Method WORLD_SEND_PARTICLE; - - private static final Method PLAYER_GET_HANDLE; - private static final Field PLAYER_CONNECTION; - private static final Method SEND_PACKET; - private static final int[] EMPTY = new int[0]; - - static { - ENUM_PARTICLE = FastReflection.nmsOptionalClass("EnumParticle").orElse(null); - SERVER_IS_1_8 = ENUM_PARTICLE != null; - - try { - Class packetParticleClass = FastReflection.nmsClass("PacketPlayOutWorldParticles"); - Class playerClass = FastReflection.nmsClass("EntityPlayer"); - Class playerConnectionClass = FastReflection.nmsClass("PlayerConnection"); - Class worldClass = FastReflection.nmsClass("WorldServer"); - Class entityPlayerClass = FastReflection.nmsClass("EntityPlayer"); - - Class craftPlayerClass = FastReflection.obcClass("entity.CraftPlayer"); - Class craftWorldClass = FastReflection.obcClass("CraftWorld"); - - if (SERVER_IS_1_8) { - PACKET_PARTICLE = packetParticleClass.getConstructor(ENUM_PARTICLE, boolean.class, float.class, - float.class, float.class, float.class, float.class, float.class, float.class, int.class, - int[].class); - WORLD_SEND_PARTICLE = worldClass.getDeclaredMethod("sendParticles", entityPlayerClass, ENUM_PARTICLE, - boolean.class, double.class, double.class, double.class, int.class, double.class, double.class, - double.class, double.class, int[].class); - } else { - PACKET_PARTICLE = packetParticleClass.getConstructor(String.class, float.class, float.class, float.class, - float.class, float.class, float.class, float.class, int.class); - WORLD_SEND_PARTICLE = worldClass.getDeclaredMethod("a", String.class, double.class, double.class, - double.class, int.class, double.class, double.class, double.class, double.class); - } - - WORLD_GET_HANDLE = craftWorldClass.getDeclaredMethod("getHandle"); - PLAYER_GET_HANDLE = craftPlayerClass.getDeclaredMethod("getHandle"); - PLAYER_CONNECTION = playerClass.getField("playerConnection"); - SEND_PACKET = playerConnectionClass.getMethod("sendPacket", FastReflection.nmsClass("Packet")); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } - - @Override - public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, - double offsetZ, double extra, Object data) { - try { - int[] datas = toData(particle, data); - - if (data instanceof Color) { - if (particle.getDataType() == Color.class) { - Color color = (Color) data; - count = 0; - offsetX = color(color.getRed()); - offsetY = color(color.getGreen()); - offsetZ = color(color.getBlue()); - extra = 1.0; - } - } - - if (receiver instanceof World) { - Object worldServer = WORLD_GET_HANDLE.invoke(receiver); - - if (SERVER_IS_1_8) { - WORLD_SEND_PARTICLE.invoke(worldServer, null, getEnumParticle(particle), true, x, y, z, count, offsetX, offsetY, offsetZ, extra, datas); - } else { - String particleName = particle.getLegacyName() + (datas.length != 2 ? "" : "_" + datas[0] + "_" + datas[1]); - WORLD_SEND_PARTICLE.invoke(worldServer, particleName, x, y, z, count, offsetX, offsetY, offsetZ, extra); - } - } else if (receiver instanceof Player) { - Object packet; - - if (SERVER_IS_1_8) { - packet = PACKET_PARTICLE.newInstance(getEnumParticle(particle), true, (float) x, (float) y, - (float) z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count, datas); - } else { - String particleName = particle.getLegacyName() + (datas.length != 2 ? "" : "_" + datas[0] + "_" + datas[1]); - packet = PACKET_PARTICLE.newInstance(particleName, (float) x, (float) y, (float) z, - (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count); - } - - Object entityPlayer = PLAYER_GET_HANDLE.invoke(receiver); - Object playerConnection = PLAYER_CONNECTION.get(entityPlayer); - SEND_PACKET.invoke(playerConnection, packet); - } - } catch (ReflectiveOperationException e) { - Iris.reportError(e); - throw new RuntimeException(e); - } - } - - @Override - public boolean isValidData(Object particle, Object data) { - return true; - } - - @Override - public Object getParticle(ParticleType particle) { - if (!SERVER_IS_1_8) { - return particle.getLegacyName(); - } - - try { - return getEnumParticle(particle); - } catch (IllegalArgumentException e) { - Iris.reportError(e); - return null; - } - } - - private Object getEnumParticle(ParticleType particleType) { - return FastReflection.enumValueOf(ENUM_PARTICLE, particleType.toString()); - } - - private int[] toData(ParticleType particle, Object data) { - Class dataType = particle.getDataType(); - if (dataType == ItemStack.class) { - if (!(data instanceof ItemStack itemStack)) { - return SERVER_IS_1_8 ? new int[2] : new int[]{1, 0}; - } - - return new int[]{itemStack.getType().getId(), itemStack.getDurability()}; - } - - if (dataType == MaterialData.class) { - if (!(data instanceof MaterialData materialData)) { - return SERVER_IS_1_8 ? new int[1] : new int[]{1, 0}; - } - - if (SERVER_IS_1_8) { - return new int[]{materialData.getItemType().getId() + (materialData.getData() << 12)}; - } else { - return new int[]{materialData.getItemType().getId(), materialData.getData()}; - } - } - - return EMPTY; - } -} diff --git a/core/src/main/java/com/volmit/iris/util/particle/ParticleType.java b/core/src/main/java/com/volmit/iris/util/particle/ParticleType.java deleted file mode 100644 index 25da1bd75..000000000 --- a/core/src/main/java/com/volmit/iris/util/particle/ParticleType.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * 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 com.volmit.iris.util.particle; - -import com.volmit.iris.Iris; -import org.bukkit.Color; -import org.bukkit.inventory.ItemStack; -import org.bukkit.material.MaterialData; - -/** - * @author MrMicky - */ -@SuppressWarnings("deprecation") -public enum ParticleType { - - // 1.7+ - EXPLOSION_NORMAL("explode", "poof"), - EXPLOSION_LARGE("largeexplode", "explosion"), - EXPLOSION_HUGE("hugeexplosion", "explosion_emitter"), - FIREWORKS_SPARK("fireworksSpark", "firework"), - WATER_BUBBLE("bubble", "bubble"), - WATER_SPLASH("splash", "splash"), - WATER_WAKE("wake", "fishing"), - SUSPENDED("suspended", "underwater"), - SUSPENDED_DEPTH("depthsuspend", "underwater"), - CRIT("crit", "crit"), - CRIT_MAGIC("magicCrit", "enchanted_hit"), - SMOKE_NORMAL("smoke", "smoke"), - SMOKE_LARGE("largesmoke", "large_smoke"), - SPELL("spell", "effect"), - SPELL_INSTANT("instantSpell", "instant_effect"), - SPELL_MOB("mobSpell", "entity_effect"), - SPELL_MOB_AMBIENT("mobSpellAmbient", "ambient_entity_effect"), - SPELL_WITCH("witchMagic", "witch"), - DRIP_WATER("dripWater", "dripping_water"), - DRIP_LAVA("dripLava", "dripping_lava"), - VILLAGER_ANGRY("angryVillager", "angry_villager"), - VILLAGER_HAPPY("happyVillager", "happy_villager"), - TOWN_AURA("townaura", "mycelium"), - NOTE("note", "note"), - PORTAL("portal", "portal"), - ENCHANTMENT_TABLE("enchantmenttable", "enchant"), - FLAME("flame", "flame"), - LAVA("lava", "lava"), - // FOOTSTEP("footstep", null), - CLOUD("cloud", "cloud"), - REDSTONE("reddust", "dust"), - SNOWBALL("snowballpoof", "item_snowball"), - SNOW_SHOVEL("snowshovel", "item_snowball"), - SLIME("slime", "item_slime"), - HEART("heart", "heart"), - ITEM_CRACK("iconcrack", "item"), - BLOCK_CRACK("blockcrack", "block"), - BLOCK_DUST("blockdust", "block"), - - // 1.8+ - BARRIER("barrier", "barrier", 8), - WATER_DROP("droplet", "rain", 8), - MOB_APPEARANCE("mobappearance", "elder_guardian", 8), - // ITEM_TAKE("take", null, 8), - - // 1.9+ - DRAGON_BREATH("dragonbreath", "dragon_breath", 9), - END_ROD("endRod", "end_rod", 9), - DAMAGE_INDICATOR("damageIndicator", "damage_indicator", 9), - SWEEP_ATTACK("sweepAttack", "sweep_attack", 9), - - // 1.10+ - FALLING_DUST("fallingdust", "falling_dust", 10), - - // 1.11+ - TOTEM("totem", "totem_of_undying", 11), - SPIT("spit", "spit", 11), - - // 1.13+ - SQUID_INK(13), - BUBBLE_POP(13), - CURRENT_DOWN(13), - BUBBLE_COLUMN_UP(13), - NAUTILUS(13), - DOLPHIN(13), - - // 1.14+ - SNEEZE(14), - CAMPFIRE_COSY_SMOKE(14), - CAMPFIRE_SIGNAL_SMOKE(14), - COMPOSTER(14), - FLASH(14), - FALLING_LAVA(14), - LANDING_LAVA(14), - FALLING_WATER(14), - - // 1.15+ - DRIPPING_HONEY(15), - FALLING_HONEY(15), - LANDING_HONEY(15), - FALLING_NECTAR(15); - - private static final int SERVER_VERSION_ID; - - static { - String ver = FastReflection.VERSION; - SERVER_VERSION_ID = ver.charAt(4) == '_' ? Character.getNumericValue(ver.charAt(3)) : Integer.parseInt(ver.substring(3, 5)); - } - - private final String legacyName; - private final String name; - private final int minimumVersion; - - // 1.7 particles - ParticleType(String legacyName, String name) { - this(legacyName, name, -1); - } - - // 1.13+ particles - ParticleType(int minimumVersion) { - this.legacyName = null; - this.name = name().toLowerCase(); - this.minimumVersion = minimumVersion; - } - - // 1.8-1.12 particles - ParticleType(String legacyName, String name, int minimumVersion) { - this.legacyName = legacyName; - this.name = name; - this.minimumVersion = minimumVersion; - } - - public static ParticleType getParticle(String particleName) { - try { - return ParticleType.valueOf(particleName.toUpperCase()); - } catch (IllegalArgumentException e) { - Iris.reportError(e); - for (ParticleType particle : values()) { - if (particle.getName().equalsIgnoreCase(particleName)) { - return particle; - } - - if (particle.hasLegacyName() && particle.getLegacyName().equalsIgnoreCase(particleName)) { - return particle; - } - } - } - return null; - } - - public boolean hasLegacyName() { - return legacyName != null; - } - - public String getLegacyName() { - if (!hasLegacyName()) { - throw new IllegalStateException("Particle " + name() + " don't have legacy name"); - } - return legacyName; - } - - public String getName() { - return name; - } - - public boolean isSupported() { - return minimumVersion <= 0 || SERVER_VERSION_ID >= minimumVersion; - } - - public Class getDataType() { - return switch (this) { - case ITEM_CRACK -> ItemStack.class; - case BLOCK_CRACK, BLOCK_DUST, FALLING_DUST -> - //noinspection deprecation - MaterialData.class; - case REDSTONE -> Color.class; - default -> Void.class; - }; - } -} From eefbbab7ee303ffb39a839ab946771fac641462b Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 18:39:11 +0200 Subject: [PATCH 067/138] remove legacy chars before minimessage deserialization --- .../volmit/iris/util/decree/DecreeSystem.java | 5 +++-- .../java/com/volmit/iris/util/format/C.java | 22 +++++++++++++++++++ .../volmit/iris/util/plugin/VolmitSender.java | 10 ++++----- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/util/decree/DecreeSystem.java b/core/src/main/java/com/volmit/iris/util/decree/DecreeSystem.java index e76f62965..d22a8b822 100644 --- a/core/src/main/java/com/volmit/iris/util/decree/DecreeSystem.java +++ b/core/src/main/java/com/volmit/iris/util/decree/DecreeSystem.java @@ -160,7 +160,8 @@ public interface DecreeSystem extends CommandExecutor, TabCompleter { } J.aBukkit(() -> { - if (!call(new VolmitSender(sender), args)) { + var volmit = new VolmitSender(sender); + if (!call(volmit, args)) { if (IrisSettings.get().getGeneral().isCommandSounds()) { if (sender instanceof Player) { @@ -169,7 +170,7 @@ public interface DecreeSystem extends CommandExecutor, TabCompleter { } } - sender.sendMessage(C.RED + "Unknown Iris Command"); + volmit.sendMessage(C.RED + "Unknown Iris Command"); } else { if (IrisSettings.get().getGeneral().isCommandSounds()) { if (sender instanceof Player) { diff --git a/core/src/main/java/com/volmit/iris/util/format/C.java b/core/src/main/java/com/volmit/iris/util/format/C.java index de1893c8f..85deb3cac 100644 --- a/core/src/main/java/com/volmit/iris/util/format/C.java +++ b/core/src/main/java/com/volmit/iris/util/format/C.java @@ -376,6 +376,28 @@ public enum C { return "#" + Integer.toHexString(spin(color.awtColor(), h, s, b).getRGB()).substring(2); } + public static String mini(String s) { + String msg = compress(s); + StringBuilder b = new StringBuilder(); + boolean c = false; + + for (char i : msg.toCharArray()) { + if (!c) { + if (i == C.COLOR_CHAR) { + c = true; + continue; + } + b.append(i); + } else { + c = false; + C o = C.getByChar(i); + b.append(o.token); + } + } + + return b.toString(); + } + public static String aura(String s, int hrad, int srad, int vrad) { return aura(s, hrad, srad, vrad, 0.3D); } diff --git a/core/src/main/java/com/volmit/iris/util/plugin/VolmitSender.java b/core/src/main/java/com/volmit/iris/util/plugin/VolmitSender.java index 217b44054..dccb95ce7 100644 --- a/core/src/main/java/com/volmit/iris/util/plugin/VolmitSender.java +++ b/core/src/main/java/com/volmit/iris/util/plugin/VolmitSender.java @@ -264,7 +264,7 @@ public class VolmitSender implements CommandSender { private Component createNoPrefixComponent(String message) { if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) { String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(message)); - return MiniMessage.miniMessage().deserialize(t); + return MiniMessage.miniMessage().deserialize(C.mini(t)); } String t = C.translateAlternateColorCodes('&', message); @@ -273,13 +273,13 @@ public class VolmitSender implements CommandSender { } private Component createNoPrefixComponentNoProcessing(String message) { - return MiniMessage.builder().postProcessor(c -> c).build().deserialize(message); + return MiniMessage.builder().postProcessor(c -> c).build().deserialize(C.mini(message)); } private Component createComponent(String message) { if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) { String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(getTag() + message)); - return MiniMessage.miniMessage().deserialize(t); + return MiniMessage.miniMessage().deserialize(C.mini(t)); } String t = C.translateAlternateColorCodes('&', getTag() + message); @@ -290,11 +290,11 @@ public class VolmitSender implements CommandSender { private Component createComponentRaw(String message) { if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) { String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(getTag() + message)); - return MiniMessage.miniMessage().deserialize(t); + return MiniMessage.miniMessage().deserialize(C.mini(t)); } String t = C.translateAlternateColorCodes('&', getTag() + message); - return MiniMessage.miniMessage().deserialize(t); + return MiniMessage.miniMessage().deserialize(C.mini(t)); } public void showWaiting(String passive, CompletableFuture f) { From f9dac8a3a1ae43684f73e71d7af505f84023abd8 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 18:40:43 +0200 Subject: [PATCH 068/138] fix compile while missing the sentry auth token --- build.gradle.kts | 2 +- core/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 24613a9f5..bb061f2bc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -147,7 +147,7 @@ tasks { group = "io.sentry" dependsOn("downloadCli") doLast { - val authToken = project.property("sentry.auth.token") ?: System.getenv("SENTRY_AUTH_TOKEN") + val authToken = project.findProperty("sentry.auth.token") ?: System.getenv("SENTRY_AUTH_TOKEN") val org = "volmit-software" val projectName = "iris" exec(cli, "releases", "new", "--auth-token", authToken, "-o", org, "-p", projectName, version) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index a6a1a20c5..cebfbc6c5 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -100,7 +100,7 @@ sentry { org = "volmit-software" projectName = "iris" - authToken = property("sentry.auth.token") as String? ?: System.getenv("SENTRY_AUTH_TOKEN") + authToken = findProperty("sentry.auth.token") as String? ?: System.getenv("SENTRY_AUTH_TOKEN") } tasks { From 172e23451475b920b8c12ba148a065352b3972e7 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 19:19:00 +0200 Subject: [PATCH 069/138] fix divide by zero in the engine svc --- .../java/com/volmit/iris/core/service/IrisEngineSVC.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java index 51351f657..df3951ea1 100644 --- a/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java @@ -187,7 +187,7 @@ public class IrisEngineSVC implements IrisService { return; try { - engine.getMantle().trim(tectonicLimit.get() / worlds.size()); + engine.getMantle().trim(tectonicLimit()); } catch (Throwable e) { Iris.reportError(e); Iris.error("EngineSVC: Failed to trim for " + name); @@ -204,7 +204,7 @@ public class IrisEngineSVC implements IrisService { try { long unloadStart = System.currentTimeMillis(); - int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / worlds.size()); + int count = engine.getMantle().unloadTectonicPlate(tectonicLimit()); if (count > 0) { Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2)); } @@ -217,6 +217,10 @@ public class IrisEngineSVC implements IrisService { } } + private int tectonicLimit() { + return tectonicLimit.get() / Math.max(worlds.size(), 1); + } + @Synchronized private void close() { if (closed) return; From 944cc19ebce2b345596dcfa8a9a2c32cac7dff22 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 19:35:43 +0200 Subject: [PATCH 070/138] use proper shuffling algorithm for the loot --- .../com/volmit/iris/engine/framework/Engine.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java index 9a87d26e9..47aa816d3 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java @@ -78,7 +78,6 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; import java.awt.Color; -import java.util.Arrays; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -457,14 +456,11 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat } } - for (int i = 0; i < 4; i++) { - try { - Arrays.parallelSort(nitems, (a, b) -> rng.nextInt()); - break; - } catch (Throwable e) { - Iris.reportError(e); - - } + for (int i = nitems.length; i > 1; i--) { + int j = rng.nextInt(i); + ItemStack tmp = nitems[i - 1]; + nitems[i - 1] = nitems[j]; + nitems[j] = tmp; } inventory.setContents(nitems); From e2a3f25dcb7516c65135663414a60d00d7f4e70c Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 19:42:03 +0200 Subject: [PATCH 071/138] fix load in parallel method --- .../java/com/volmit/iris/core/loader/ResourceLoader.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java b/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java index 6575abd37..a9b468e57 100644 --- a/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java +++ b/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java @@ -45,6 +45,7 @@ import lombok.ToString; import java.io.*; import java.util.Locale; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; @@ -240,8 +241,10 @@ public class ResourceLoader implements MeteredCache { for (String i : s) { burst.queue(() -> { T t = load(i); + if (t == null) + return; - if (t != null) { + synchronized (m) { m.add(t); } }); From 9316ea9e5b514742187c0bf340d386b738513d6c Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 19:49:06 +0200 Subject: [PATCH 072/138] fix npe when creating cuboids --- .../volmit/iris/core/commands/CommandObject.java | 15 ++++++++++++++- .../com/volmit/iris/core/service/WandSVC.java | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandObject.java b/core/src/main/java/com/volmit/iris/core/commands/CommandObject.java index 899ff7270..ccbf91c11 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandObject.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandObject.java @@ -241,7 +241,8 @@ public class CommandObject implements DecreeExecutor { Location[] b = WandSVC.getCuboid(player()); - if (b == null) { + if (b == null || b[0] == null || b[1] == null) { + sender().sendMessage("No area selected."); return; } Location a1 = b[0].clone(); @@ -417,6 +418,10 @@ public class CommandObject implements DecreeExecutor { } Location[] b = WandSVC.getCuboid(player()); + if (b == null || b[0] == null || b[1] == null) { + sender().sendMessage("No area selected."); + return; + } Location a1 = b[0].clone(); Location a2 = b[1].clone(); Direction d = Direction.closest(player().getLocation().getDirection()).reverse(); @@ -477,6 +482,10 @@ public class CommandObject implements DecreeExecutor { } Location[] b = WandSVC.getCuboid(player()); + if (b == null || b[0] == null || b[1] == null) { + sender().sendMessage("No area selected."); + return; + } Location a1 = b[0].clone(); Location a2 = b[1].clone(); Location a1x = b[0].clone(); @@ -524,6 +533,10 @@ public class CommandObject implements DecreeExecutor { } Location[] b = WandSVC.getCuboid(player()); + if (b == null || b[0] == null || b[1] == null) { + sender().sendMessage("No area selected."); + return; + } b[0].add(new Vector(0, 1, 0)); b[1].add(new Vector(0, 1, 0)); Location a1 = b[0].clone(); diff --git a/core/src/main/java/com/volmit/iris/core/service/WandSVC.java b/core/src/main/java/com/volmit/iris/core/service/WandSVC.java index 53d3800ac..95ea520f0 100644 --- a/core/src/main/java/com/volmit/iris/core/service/WandSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/WandSVC.java @@ -51,6 +51,7 @@ import org.bukkit.util.Vector; import java.awt.Color; import java.util.ArrayList; +import java.util.Arrays; import java.util.Objects; import java.util.concurrent.CountDownLatch; @@ -80,6 +81,8 @@ public class WandSVC implements IrisService { try { Location[] f = getCuboid(p); + if (f == null || f[0] == null || f[1] == null) + return null; Cuboid c = new Cuboid(f[0], f[1]); IrisObject s = new IrisObject(c.getSizeX(), c.getSizeY(), c.getSizeZ()); @@ -357,6 +360,7 @@ public class WandSVC implements IrisService { try { if ((IrisSettings.get().getWorld().worldEditWandCUI && isHoldingWand(p)) || isWand(p.getInventory().getItemInMainHand())) { Location[] d = getCuboid(p); + if (d == null || d[0] == null || d[1] == null) return; new WandSelection(new Cuboid(d[0], d[1]), p).draw(); } } catch (Throwable e) { From cd80acdc7d77b4029795ae51282411e02df82a0d Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 19:50:50 +0200 Subject: [PATCH 073/138] fix IndexOutOfBoundsException when getting the selection from the wand --- core/src/main/java/com/volmit/iris/core/service/WandSVC.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/core/service/WandSVC.java b/core/src/main/java/com/volmit/iris/core/service/WandSVC.java index 95ea520f0..8d984ba91 100644 --- a/core/src/main/java/com/volmit/iris/core/service/WandSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/WandSVC.java @@ -201,7 +201,9 @@ public class WandSVC implements IrisService { public static Location stringToLocation(String s) { try { String[] f = s.split("\\Q in \\E"); + if (f.length != 2) return null; String[] g = f[0].split("\\Q,\\E"); + if (g.length != 3) return null; return new Location(Bukkit.getWorld(f[1]), Integer.parseInt(g[0]), Integer.parseInt(g[1]), Integer.parseInt(g[2])); } catch (Throwable e) { Iris.reportError(e); From 113f25dab8c50e89a4f88771070050ddcdb80184 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 23:17:47 +0200 Subject: [PATCH 074/138] more sentry context --- core/src/main/java/com/volmit/iris/Iris.java | 3 +++ .../iris/core/commands/CommandDeveloper.java | 3 +++ .../volmit/iris/util/context/IrisContext.java | 17 +++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 43384f127..ab9c5dc31 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -44,6 +44,7 @@ import com.volmit.iris.core.safeguard.UtilsSFG; import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.context.IrisContext; import com.volmit.iris.util.exceptions.IrisException; import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; @@ -963,6 +964,8 @@ public class Iris extends VolmitPlugin implements Listener { options.setBeforeSend((event, hint) -> { event.setTag("iris.safeguard", IrisSafeguard.mode()); event.setTag("iris.nms", INMS.get().getClass().getCanonicalName()); + var context = IrisContext.get(); + if (context != null) event.getContexts().set("engine", context.asContext()); return event; }); }); diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java index 29d4fd80f..e96eb29ed 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java @@ -26,6 +26,7 @@ import com.volmit.iris.core.tools.IrisPackBenchmarking; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.IrisDimension; +import com.volmit.iris.util.context.IrisContext; import com.volmit.iris.util.decree.DecreeExecutor; import com.volmit.iris.util.decree.DecreeOrigin; import com.volmit.iris.util.decree.annotations.Decree; @@ -73,6 +74,8 @@ public class CommandDeveloper implements DecreeExecutor { @Decree(description = "Send a test exception to sentry") public void Sentry() { + Engine engine = engine(); + if (engine != null) IrisContext.getOr(engine); Iris.reportError(new Exception("This is a test")); } diff --git a/core/src/main/java/com/volmit/iris/util/context/IrisContext.java b/core/src/main/java/com/volmit/iris/util/context/IrisContext.java index 8b69eb867..b076b5eef 100644 --- a/core/src/main/java/com/volmit/iris/util/context/IrisContext.java +++ b/core/src/main/java/com/volmit/iris/util/context/IrisContext.java @@ -87,4 +87,21 @@ public class IrisContext { public IrisComplex getComplex() { return engine.getComplex(); } + + public KMap asContext() { + var hash32 = engine.getHash32().getNow(null); + var dimension = engine.getDimension(); + var mantle = engine.getMantle(); + return new KMap() + .qput("studio", engine.isStudio()) + .qput("closed", engine.isClosed()) + .qput("pack", new KMap<>() + .qput("key", dimension.getLoadKey()) + .qput("version", dimension.getVersion()) + .qput("hash", hash32 == null ? "" : Long.toHexString(hash32))) + .qput("mantle", new KMap<>() + .qput("idle", mantle.getAdjustedIdleDuration()) + .qput("loaded", mantle.getLoadedRegionCount()) + .qput("queued", mantle.getUnloadRegionCount())); + } } From ccc3bab8e06e5e554fbd8068916f718a239d5cf4 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 23:25:33 +0200 Subject: [PATCH 075/138] allow carving recursion --- .../components/MantleCarvingComponent.java | 8 +++---- .../iris/engine/object/IrisCarving.java | 23 ++++++++++++------- .../volmit/iris/engine/object/IrisCave.java | 10 ++++---- .../iris/engine/object/IrisCavePlacer.java | 14 +++++++---- .../volmit/iris/engine/object/IrisRavine.java | 10 ++++---- .../iris/engine/object/IrisRavinePlacer.java | 14 +++++++---- 6 files changed, 47 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleCarvingComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleCarvingComponent.java index 31762626f..8d067abfd 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleCarvingComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleCarvingComponent.java @@ -58,21 +58,21 @@ public class MantleCarvingComponent extends IrisMantleComponent { @ChunkCoordinates private void carve(IrisCarving carving, MantleWriter writer, RNG rng, int cx, int cz) { - carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4); + carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4, 0); } private int computeRadius() { var dimension = getDimension(); int max = 0; - max = Math.max(max, dimension.getCarving().getMaxRange(getData())); + max = Math.max(max, dimension.getCarving().getMaxRange(getData(), 0)); for (var i : dimension.getAllRegions(this::getData)) { - max = Math.max(max, i.getCarving().getMaxRange(getData())); + max = Math.max(max, i.getCarving().getMaxRange(getData(), 0)); } for (var i : dimension.getAllBiomes(this::getData)) { - max = Math.max(max, i.getCarving().getMaxRange(getData())); + max = Math.max(max, i.getCarving().getMaxRange(getData(), 0)); } return max; diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCarving.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCarving.java index bf7efa6af..1da8b1a31 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCarving.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCarving.java @@ -61,21 +61,25 @@ public class IrisCarving { @BlockCoordinates - public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) { - doCarving(writer, rng, engine, x, y, z, -1); + public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int depth) { + doCarving(writer, rng, engine, x, y, z, depth, -1); } @BlockCoordinates - public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int waterHint) { + public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) { + int nextRecursion = recursion + 1; + if (caves.isNotEmpty()) { for (IrisCavePlacer i : caves) { - i.generateCave(writer, rng, engine, x, y, z, waterHint); + if (recursion > i.getMaxRecursion()) continue; + i.generateCave(writer, rng, engine, x, y, z, nextRecursion, waterHint); } } if (ravines.isNotEmpty()) { for (IrisRavinePlacer i : ravines) { - i.generateRavine(writer, rng, engine, x, y, z, waterHint); + if (recursion > i.getMaxRecursion()) continue; + i.generateRavine(writer, rng, engine, x, y, z, nextRecursion, waterHint); } } @@ -104,15 +108,18 @@ public class IrisCarving { } } - public int getMaxRange(IrisData data) { + public int getMaxRange(IrisData data, int recursion) { int max = 0; + int nextRecursion = recursion + 1; for (IrisCavePlacer i : caves) { - max = Math.max(max, i.getSize(data)); + if (recursion > i.getMaxRecursion()) continue; + max = Math.max(max, i.getSize(data, nextRecursion)); } for (IrisRavinePlacer i : ravines) { - max = Math.max(max, i.getSize(data)); + if (recursion > i.getMaxRecursion()) continue; + max = Math.max(max, i.getSize(data, nextRecursion)); } if (elipsoids.isNotEmpty()) { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java index 32ab22439..e0e818403 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java @@ -66,10 +66,10 @@ public class IrisCave extends IrisRegistrant { } public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) { - generate(writer, rng, engine, x, y, z, -1); + generate(writer, rng, engine, x, y, z, 0, -1); } - public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int waterHint) { + public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) { double girth = getWorm().getGirth().get(rng, x, z, engine.getData()); KList points = getWorm().generate(rng, engine.getData(), writer, verticalRange, x, y, z, (at) -> { @@ -92,7 +92,7 @@ public class IrisCave extends IrisRegistrant { int h = Math.min(Math.max(highestWater, waterHint), engine.getDimension().getFluidHeight()); for (IrisPosition i : points) { - fork.doCarving(writer, rng, engine, i.getX(), i.getY(), i.getZ(), h); + fork.doCarving(writer, rng, engine, i.getX(), i.getY(), i.getZ(), recursion, h); } MatterCavern c = new MatterCavern(true, customBiome, (byte) 0); @@ -108,7 +108,7 @@ public class IrisCave extends IrisRegistrant { } - public int getMaxSize(IrisData data) { - return getWorm().getMaxDistance() + fork.getMaxRange(data); + public int getMaxSize(IrisData data, int depth) { + return getWorm().getMaxDistance() + fork.getMaxRange(data, depth); } } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java index acaa55904..1a95493a1 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java @@ -50,6 +50,10 @@ public class IrisCavePlacer implements IRare { @Desc("The cave to place") @RegistryListResource(IrisCave.class) private String cave; + @MinNumber(1) + @MaxNumber(256) + @Desc("The maximum recursion depth") + private int maxRecursion = 16; @Desc("If set to true, this cave is allowed to break the surface") private boolean breakSurface = true; @Desc("The height range this cave can spawn at. If breakSurface is false, the output of this range will be clamped by the current world height to prevent surface breaking.") @@ -60,10 +64,10 @@ public class IrisCavePlacer implements IRare { } public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) { - generateCave(mantle, rng, engine, x, y, z, -1); + generateCave(mantle, rng, engine, x, y, z, 0, -1); } - public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int waterHint) { + public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) { if (fail.get()) { return; } @@ -92,18 +96,18 @@ public class IrisCavePlacer implements IRare { } try { - cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), waterHint); + cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), recursion, waterHint); } catch (Throwable e) { e.printStackTrace(); fail.set(true); } } - public int getSize(IrisData data) { + public int getSize(IrisData data, int depth) { IrisCave cave = getRealCave(data); if (cave != null) { - return cave.getMaxSize(data); + return cave.getMaxSize(data, depth); } return 32; diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisRavine.java b/core/src/main/java/com/volmit/iris/engine/object/IrisRavine.java index ca6bb7bd4..e2d9a79e4 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisRavine.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisRavine.java @@ -93,10 +93,10 @@ public class IrisRavine extends IrisRegistrant { } public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) { - generate(writer, rng, engine, x, y, z, -1); + generate(writer, rng, engine, x, y, z, 0, -1); } - public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int waterHint) { + public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) { KList pos = getWorm().generate(rng, engine.getData(), writer, null, x, y, z, (at) -> { }); CNG dg = depthStyle.getGenerator().createNoCache(rng, engine.getData()); @@ -135,7 +135,7 @@ public class IrisRavine extends IrisRegistrant { int width = (int) Math.round(bw.fitDouble(baseWidthStyle.getMin(), baseWidthStyle.getMax(), p.getX(), p.getZ())); int surface = (int) Math.round(rsurface - depth * 0.45); - fork.doCarving(writer, rng, engine, p.getX(), rng.i(surface - depth, surface), p.getZ(), Math.max(highestWater, waterHint)); + fork.doCarving(writer, rng, engine, p.getX(), rng.i(surface - depth, surface), p.getZ(), recursion, Math.max(highestWater, waterHint)); for (int i = surface + depth; i >= surface; i--) { if (i % ribThickness == 0) { @@ -184,7 +184,7 @@ public class IrisRavine extends IrisRegistrant { } - public int getMaxSize(IrisData data) { - return getWorm().getMaxDistance() + fork.getMaxRange(data); + public int getMaxSize(IrisData data, int depth) { + return getWorm().getMaxDistance() + fork.getMaxRange(data, depth); } } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisRavinePlacer.java b/core/src/main/java/com/volmit/iris/engine/object/IrisRavinePlacer.java index 570859a63..fa65c5c79 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisRavinePlacer.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisRavinePlacer.java @@ -50,16 +50,20 @@ public class IrisRavinePlacer implements IRare { @Desc("The ravine to place") @RegistryListResource(IrisRavine.class) private String ravine; + @MinNumber(1) + @MaxNumber(256) + @Desc("The maximum recursion depth") + private int maxRecursion = 100; public IrisRavine getRealRavine(IrisData data) { return ravineCache.aquire(() -> data.getRavineLoader().load(getRavine())); } public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) { - generateRavine(mantle, rng, engine, x, y, z, -1); + generateRavine(mantle, rng, engine, x, y, z, 0, -1); } - public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int waterHint) { + public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) { if (fail.get()) { return; } @@ -80,14 +84,14 @@ public class IrisRavinePlacer implements IRare { try { int xx = x + rng.nextInt(15); int zz = z + rng.nextInt(15); - ravine.generate(mantle, rng, engine, xx, y, zz, waterHint); + ravine.generate(mantle, rng, engine, xx, y, zz, recursion, waterHint); } catch (Throwable e) { e.printStackTrace(); fail.set(true); } } - public int getSize(IrisData data) { - return getRealRavine(data).getMaxSize(data); + public int getSize(IrisData data, int depth) { + return getRealRavine(data).getMaxSize(data, depth); } } From 8712c8874ccd417cd199ca123bbbe119aa78a53c Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 9 Jun 2025 23:27:12 +0200 Subject: [PATCH 076/138] disable global pregen cache by default for now --- core/src/main/java/com/volmit/iris/core/IrisSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 20c0fa265..7804b3eac 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -132,7 +132,7 @@ public class IrisSettings { public boolean markerEntitySpawningSystem = true; public boolean effectSystem = true; public boolean worldEditWandCUI = true; - public boolean globalPregenCache = true; + public boolean globalPregenCache = false; } @Data From b62ac875d590b6caa481efe9a7695f4120d04c05 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 10 Jun 2025 16:48:00 +0200 Subject: [PATCH 077/138] cleanup gradle --- build.gradle.kts | 10 +++++++ core/build.gradle.kts | 48 +++++++++++++----------------- core/src/main/resources/plugin.yml | 18 +---------- 3 files changed, 32 insertions(+), 44 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index bb061f2bc..272e924e7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -89,6 +89,7 @@ nmsBindings.forEach { key, value -> dependencies { compileOnly(project(":core")) compileOnly("org.jetbrains:annotations:26.0.2") + compileOnly("net.bytebuddy:byte-buddy:1.17.5") } } @@ -103,6 +104,7 @@ nmsBindings.forEach { key, value -> systemProperty("disable.watchdog", "") systemProperty("net.kyori.ansi.colorLevel", color) systemProperty("com.mojang.eula.agree", true) + jvmArgs("-javaagent:${tasks.jar.flatMap { it.archiveFile }.get().asFile.absolutePath}") } } @@ -114,6 +116,13 @@ tasks { } from(project(":core").tasks.shadowJar.flatMap { it.archiveFile }.map { zipTree(it) }) archiveFileName.set("Iris-${project.version}.jar") + + manifest.attributes( + "Agent-Class" to "com.volmit.iris.util.agent.Installer", + "Premain-Class" to "com.volmit.iris.util.agent.Installer", + "Can-Redefine-Classes" to true, + "Can-Retransform-Classes" to true + ) } register("iris") { @@ -191,6 +200,7 @@ allprojects { maven("https://repo.mineinabyss.com/releases") maven("https://hub.jeff-media.com/nexus/repository/jeff-media-public/") maven("https://repo.nexomc.com/releases/") + maven("https://nexus.phoenixdevt.fr/repository/maven-public/") } dependencies { diff --git a/core/build.gradle.kts b/core/build.gradle.kts index cebfbc6c5..836bd2f7b 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -26,10 +26,8 @@ plugins { val apiVersion = "1.19" val main = "com.volmit.iris.Iris" -repositories { - maven("https://nexus.phoenixdevt.fr/repository/maven-public/") - maven("https://repo.auxilor.io/repository/maven-public/") -} +val dynamic: Configuration by configurations.creating +configurations.compileOnly { extendsFrom(dynamic) } /** * Dependencies. @@ -48,10 +46,6 @@ dependencies { compileOnly("org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT") compileOnly("org.apache.logging.log4j:log4j-api:2.19.0") compileOnly("org.apache.logging.log4j:log4j-core:2.19.0") - compileOnly("commons-io:commons-io:2.13.0") - compileOnly("commons-lang:commons-lang:2.6") - compileOnly("com.github.oshi:oshi-core:5.8.5") - compileOnly("org.lz4:lz4-java:1.8.0") // Third Party Integrations compileOnly("com.nexomc:nexo:1.6.0") @@ -60,9 +54,10 @@ dependencies { compileOnly("com.github.Ssomar-Developement:SCore:4.23.10.8") compileOnly("net.Indyuce:MMOItems-API:6.9.5-SNAPSHOT") compileOnly("com.willfp:EcoItems:5.44.0") + compileOnly("io.lumine:Mythic-Dist:5.2.1") + compileOnly("io.lumine:MythicCrucible-Dist:2.0.0") //implementation files("libs/CustomItems.jar") - // Shaded implementation("com.dfsek:paralithic:0.8.1") implementation("io.papermc:paperlib:1.0.5") @@ -71,24 +66,22 @@ dependencies { implementation("net.kyori:adventure-api:4.17.0") implementation("org.bstats:bstats-bukkit:3.1.0") - //implementation("org.bytedeco:javacpp:1.5.10") - //implementation("org.bytedeco:cuda-platform:12.3-8.9-1.5.10") - compileOnly("io.lumine:Mythic-Dist:5.2.1") - compileOnly("io.lumine:MythicCrucible-Dist:2.0.0") - // Dynamically Loaded - compileOnly("io.timeandspace:smoothie-map:2.0.2") - compileOnly("it.unimi.dsi:fastutil:8.5.8") - compileOnly("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2") - compileOnly("org.zeroturnaround:zt-zip:1.14") - compileOnly("com.google.code.gson:gson:2.10.1") - compileOnly("org.ow2.asm:asm:9.2") - compileOnly("com.google.guava:guava:33.0.0-jre") - compileOnly("bsf:bsf:2.4.0") - compileOnly("rhino:js:1.7R2") - compileOnly("com.github.ben-manes.caffeine:caffeine:3.0.6") - compileOnly("org.apache.commons:commons-lang3:3.12.0") - compileOnly("com.github.oshi:oshi-core:6.6.5") + dynamic("commons-io:commons-io:2.13.0") + dynamic("commons-lang:commons-lang:2.6") + dynamic("com.github.oshi:oshi-core:6.6.5") + dynamic("org.lz4:lz4-java:1.8.0") + dynamic("it.unimi.dsi:fastutil:8.5.8") + dynamic("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2") + dynamic("org.zeroturnaround:zt-zip:1.14") + dynamic("com.google.code.gson:gson:2.10.1") + dynamic("org.ow2.asm:asm:9.8") + dynamic("bsf:bsf:2.4.0") + dynamic("rhino:js:1.7R2") + dynamic("com.github.ben-manes.caffeine:caffeine:3.0.6") + dynamic("org.apache.commons:commons-lang3:3.12.0") + dynamic("net.bytebuddy:byte-buddy:1.17.5") + dynamic("net.bytebuddy:byte-buddy-agent:1.17.5") } java { @@ -120,7 +113,8 @@ tasks { "name" to rootProject.name, "version" to rootProject.version, "apiVersion" to apiVersion, - "main" to main + "main" to main, + "libraries" to dynamic.allDependencies.map { "\n - $it" }.sorted().joinToString("") ) filesMatching("**/plugin.yml") { expand(inputs.properties) diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml index d742560ac..89fbfb449 100644 --- a/core/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -5,23 +5,7 @@ load: STARTUP authors: [ cyberpwn, NextdoorPsycho, Vatuu ] website: volmit.com description: More than a Dimension! -libraries: - - com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2 - - com.github.ben-manes.caffeine:caffeine:3.0.5 - - org.apache.commons:commons-lang3:3.12.0 - - commons-io:commons-io:2.13.0 - - io.timeandspace:smoothie-map:2.0.2 - - com.google.guava:guava:31.0.1-jre - - com.google.code.gson:gson:2.10.1 - - org.zeroturnaround:zt-zip:1.14 - - it.unimi.dsi:fastutil:8.5.6 - - org.ow2.asm:asm:9.2 - - rhino:js:1.7R2 - - bsf:bsf:2.4.0 - - org.lz4:lz4-java:1.8.0 - - com.github.oshi:oshi-core:6.6.5 - - net.bytebuddy:byte-buddy:1.17.5 - - net.bytebuddy:byte-buddy-agent:1.17.5 +libraries: ${libraries} commands: iris: aliases: [ ir, irs ] From ed67b4d3c20cf2213ef2a831f601b1dc966b810c Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 11 Jun 2025 13:03:31 +0200 Subject: [PATCH 078/138] fix spawners not having entities due to using old format --- .../com/volmit/iris/engine/object/IrisBlockData.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisBlockData.java b/core/src/main/java/com/volmit/iris/engine/object/IrisBlockData.java index 65a9a59a8..f3f162a7d 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisBlockData.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisBlockData.java @@ -19,6 +19,7 @@ package com.volmit.iris.engine.object; import com.volmit.iris.Iris; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisRegistrant; import com.volmit.iris.core.nms.INMS; @@ -34,8 +35,8 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import org.bukkit.Material; import org.bukkit.block.data.BlockData; -import org.bukkit.entity.EntityType; import java.util.Map; @@ -202,6 +203,14 @@ public class IrisBlockData extends IrisRegistrant { public TileData tryGetTile(IrisData data) { //TODO Do like a registry thing with the tile data registry. Also update the parsing of data to include **block** entities. var type = getBlockData(data).getMaterial(); + if (type == Material.SPAWNER && this.data.containsKey("entitySpawn")) { + String id = (String) this.data.get("entitySpawn"); + if (tileData == null) tileData = new KMap<>(); + KMap spawnData = (KMap) tileData.computeIfAbsent("SpawnData", k -> new KMap<>()); + KMap entity = (KMap) spawnData.computeIfAbsent("entity", k -> new KMap<>()); + entity.putIfAbsent("id", Identifier.fromString(id).toString()); + } + if (!INMS.get().hasTile(type) || tileData == null || tileData.isEmpty()) return null; return new TileData(type, this.tileData); From 37be7ca847290ee26e50ac3943556e6aeca614b8 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 11 Jun 2025 13:14:28 +0200 Subject: [PATCH 079/138] don't send json and zip file closed exceptions to sentry --- core/src/main/java/com/volmit/iris/Iris.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index ab9c5dc31..3192a2a61 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -53,6 +53,7 @@ import com.volmit.iris.util.io.FileWatcher; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.io.InstanceState; import com.volmit.iris.util.io.JarScanner; +import com.volmit.iris.util.json.JSONException; import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.misc.getHardware; @@ -947,6 +948,10 @@ public class Iris extends VolmitPlugin implements Listener { } } + private static boolean suppress(Throwable e) { + return (e instanceof IllegalStateException ex && "zip file closed".equals(ex.getMessage())) || e instanceof JSONException; + } + private static void setupSentry() { var settings = IrisSettings.get().getSentry(); if (settings.disableAutoReporting || Sentry.isEnabled()) return; @@ -962,6 +967,7 @@ public class Iris extends VolmitPlugin implements Listener { options.setEnableUncaughtExceptionHandler(false); options.setRelease(Iris.instance.getDescription().getVersion()); options.setBeforeSend((event, hint) -> { + if (suppress(event.getThrowable())) return null; event.setTag("iris.safeguard", IrisSafeguard.mode()); event.setTag("iris.nms", INMS.get().getClass().getCanonicalName()); var context = IrisContext.get(); From 851ac18f0d29ed22d54a340867d8416f7cbb20f4 Mon Sep 17 00:00:00 2001 From: Julian Krings <47589149+CrazyDev05@users.noreply.github.com> Date: Wed, 11 Jun 2025 13:23:09 +0200 Subject: [PATCH 080/138] implement custom conditions for mythic mobs (#1204) --- .../volmit/iris/core/link/MythicMobsLink.java | 81 +++++++++++++++++-- .../com/volmit/iris/engine/IrisEngine.java | 4 +- .../volmit/iris/engine/framework/Engine.java | 4 +- 3 files changed, 80 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/link/MythicMobsLink.java b/core/src/main/java/com/volmit/iris/core/link/MythicMobsLink.java index 8a7d1d7be..052952346 100644 --- a/core/src/main/java/com/volmit/iris/core/link/MythicMobsLink.java +++ b/core/src/main/java/com/volmit/iris/core/link/MythicMobsLink.java @@ -18,20 +18,33 @@ package com.volmit.iris.core.link; +import com.google.common.collect.Sets; +import com.volmit.iris.Iris; +import com.volmit.iris.core.tools.IrisToolbelt; +import io.lumine.mythic.api.adapters.AbstractLocation; +import io.lumine.mythic.api.config.MythicLineConfig; +import io.lumine.mythic.api.skills.conditions.ILocationCondition; import io.lumine.mythic.bukkit.MythicBukkit; +import io.lumine.mythic.bukkit.adapters.BukkitWorld; +import io.lumine.mythic.bukkit.events.MythicConditionLoadEvent; +import io.lumine.mythic.core.skills.SkillCondition; +import io.lumine.mythic.core.utils.annotations.MythicCondition; +import io.lumine.mythic.core.utils.annotations.MythicField; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Entity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; import org.bukkit.plugin.Plugin; import javax.annotation.Nullable; -import java.util.Collection; -import java.util.List; +import java.util.*; public class MythicMobsLink { public MythicMobsLink() { - + if (getPlugin() == null) return; + Iris.instance.registerListener(new ConditionListener()); } public boolean isEnabled() { @@ -49,12 +62,70 @@ public class MythicMobsLink { * @param location The location * @return The mob, or null if it can't be spawned */ - public @Nullable - Entity spawnMob(String mob, Location location) { + public @Nullable Entity spawnMob(String mob, Location location) { return isEnabled() ? MythicBukkit.inst().getMobManager().spawnMob(mob, location).getEntity().getBukkitEntity() : null; } public Collection getMythicMobTypes() { return isEnabled() ? MythicBukkit.inst().getMobManager().getMobNames() : List.of(); } + + private static class ConditionListener implements Listener { + @EventHandler + public void on(MythicConditionLoadEvent event) { + switch (event.getConditionName()) { + case "irisbiome" -> event.register(new IrisBiomeCondition(event.getConditionName(), event.getConfig())); + case "irisregion" -> event.register(new IrisRegionCondition(event.getConditionName(), event.getConfig())); + } + } + } + + @MythicCondition(author = "CrazyDev22", name = "irisbiome", description = "Tests if the target is within the given list of biomes") + public static class IrisBiomeCondition extends SkillCondition implements ILocationCondition { + @MythicField(name = "biome", aliases = {"b"}, description = "A list of biomes to check") + private Set biomes = Sets.newConcurrentHashSet(); + @MythicField(name = "surface", aliases = {"s"}, description = "If the biome check should only be performed on the surface") + private boolean surface; + + public IrisBiomeCondition(String line, MythicLineConfig mlc) { + super(line); + String b = mlc.getString(new String[]{"biome", "b"}, ""); + biomes.addAll(Arrays.asList(b.split(","))); + surface = mlc.getBoolean(new String[]{"surface", "s"}, false); + } + + @Override + public boolean check(AbstractLocation target) { + var access = IrisToolbelt.access(((BukkitWorld) target.getWorld()).getBukkitWorld()); + if (access == null) return false; + var engine = access.getEngine(); + if (engine == null) return false; + var biome = surface ? + engine.getSurfaceBiome(target.getBlockX(), target.getBlockZ()) : + engine.getBiomeOrMantle(target.getBlockX(), target.getBlockY() - engine.getMinHeight(), target.getBlockZ()); + return biomes.contains(biome.getLoadKey()); + } + } + + @MythicCondition(author = "CrazyDev22", name = "irisbiome", description = "Tests if the target is within the given list of biomes") + public static class IrisRegionCondition extends SkillCondition implements ILocationCondition { + @MythicField(name = "region", aliases = {"r"}, description = "A list of regions to check") + private Set regions = Sets.newConcurrentHashSet(); + + public IrisRegionCondition(String line, MythicLineConfig mlc) { + super(line); + String b = mlc.getString(new String[]{"region", "r"}, ""); + regions.addAll(Arrays.asList(b.split(","))); + } + + @Override + public boolean check(AbstractLocation target) { + var access = IrisToolbelt.access(((BukkitWorld) target.getWorld()).getBukkitWorld()); + if (access == null) return false; + var engine = access.getEngine(); + if (engine == null) return false; + var region = engine.getRegion(target.getBlockX(), target.getBlockZ()); + return regions.contains(region.getLoadKey()); + } + } } diff --git a/core/src/main/java/com/volmit/iris/engine/IrisEngine.java b/core/src/main/java/com/volmit/iris/engine/IrisEngine.java index 7224ea295..2f9bd5a88 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisEngine.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisEngine.java @@ -96,7 +96,6 @@ public class IrisEngine implements Engine { private EngineExecutionEnvironment execution; private EngineWorldManager worldManager; private volatile int parallelism; - private volatile int minHeight; private boolean failing; private boolean closed; private int cacheId; @@ -129,7 +128,6 @@ public class IrisEngine implements Engine { getData().setEngine(this); getData().loadPrefetch(this); Iris.info("Initializing Engine: " + target.getWorld().name() + "/" + target.getDimension().getLoadKey() + " (" + target.getDimension().getDimensionHeight() + " height) Seed: " + getSeedManager().getSeed()); - minHeight = 0; failing = false; closed = false; art = J.ar(this::tickRandomPlayer, 0); @@ -475,7 +473,7 @@ public class IrisEngine implements Engine { getEngineData().getStatistics().generatedChunk(); try { PrecisionStopwatch p = PrecisionStopwatch.start(); - Hunk blocks = vblocks.listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y + getMinHeight(), z + zz, t)); + Hunk blocks = vblocks.listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y, z + zz, t)); if (getDimension().isDebugChunkCrossSections() && ((x >> 4) % getDimension().getDebugCrossSectionsMod() == 0 || (z >> 4) % getDimension().getDebugCrossSectionsMod() == 0)) { for (int i = 0; i < 16; i++) { diff --git a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java index 47aa816d3..010628b7d 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java @@ -140,7 +140,9 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat return getTarget().getWorld().minHeight(); } - void setMinHeight(int min); + default void setMinHeight(int min) { + getTarget().getWorld().minHeight(min); + } @BlockCoordinates default void generate(int x, int z, TerrainChunk tc, boolean multicore) throws WrongEngineBroException { From 7b7118fe0dd095aa844d579bc19f4db61c1d5916 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 13 Jun 2025 12:26:56 +0200 Subject: [PATCH 081/138] add id for servers hash of jvm name & version, processor info, memory size and plugins --- core/src/main/java/com/volmit/iris/Iris.java | 1975 +++++++++-------- .../com/volmit/iris/core/IrisSettings.java | 1 + .../com/volmit/iris/util/sentry/ServerID.java | 59 + 3 files changed, 1049 insertions(+), 986 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/util/sentry/ServerID.java diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 3192a2a61..08d5c9736 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -1,986 +1,989 @@ -/* - * 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 com.volmit.iris; - -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import com.volmit.iris.core.IrisSettings; -import com.volmit.iris.core.ServerConfigurator; -import com.volmit.iris.core.link.IrisPapiExpansion; -import com.volmit.iris.core.link.MultiverseCoreLink; -import com.volmit.iris.core.link.MythicMobsLink; -import com.volmit.iris.core.loader.IrisData; -import com.volmit.iris.core.nms.INMS; -import com.volmit.iris.core.nms.v1X.NMSBinding1X; -import com.volmit.iris.core.pregenerator.LazyPregenerator; -import com.volmit.iris.core.service.StudioSVC; -import com.volmit.iris.core.tools.IrisToolbelt; -import com.volmit.iris.engine.EnginePanic; -import com.volmit.iris.engine.object.IrisCompat; -import com.volmit.iris.engine.object.IrisContextInjector; -import com.volmit.iris.engine.object.IrisDimension; -import com.volmit.iris.engine.object.IrisWorld; -import com.volmit.iris.engine.platform.BukkitChunkGenerator; -import com.volmit.iris.engine.platform.DummyChunkGenerator; -import com.volmit.iris.core.safeguard.IrisSafeguard; -import com.volmit.iris.core.safeguard.UtilsSFG; -import com.volmit.iris.engine.platform.PlatformChunkGenerator; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.collection.KMap; -import com.volmit.iris.util.context.IrisContext; -import com.volmit.iris.util.exceptions.IrisException; -import com.volmit.iris.util.format.C; -import com.volmit.iris.util.format.Form; -import com.volmit.iris.util.function.NastyRunnable; -import com.volmit.iris.util.io.FileWatcher; -import com.volmit.iris.util.io.IO; -import com.volmit.iris.util.io.InstanceState; -import com.volmit.iris.util.io.JarScanner; -import com.volmit.iris.util.json.JSONException; -import com.volmit.iris.util.math.M; -import com.volmit.iris.util.math.RNG; -import com.volmit.iris.util.misc.getHardware; -import com.volmit.iris.util.parallel.MultiBurst; -import com.volmit.iris.util.plugin.IrisService; -import com.volmit.iris.util.plugin.VolmitPlugin; -import com.volmit.iris.util.plugin.VolmitSender; -import com.volmit.iris.util.reflect.ShadeFix; -import com.volmit.iris.util.scheduling.J; -import com.volmit.iris.util.scheduling.Queue; -import com.volmit.iris.util.scheduling.ShurikenQueue; -import com.volmit.iris.util.sentry.Attachments; -import com.volmit.iris.util.sentry.IrisLogger; -import io.papermc.lib.PaperLib; -import io.sentry.Sentry; -import net.kyori.adventure.platform.bukkit.BukkitAudiences; -import net.kyori.adventure.text.serializer.ComponentSerializer; -import org.bstats.bukkit.Metrics; -import org.bstats.charts.DrilldownPie; -import org.bstats.charts.SimplePie; -import org.bstats.charts.SingleLineChart; -import org.bukkit.*; -import org.bukkit.block.data.BlockData; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.*; -import org.bukkit.generator.BiomeProvider; -import org.bukkit.generator.ChunkGenerator; -import org.bukkit.plugin.IllegalPluginAccessException; -import org.bukkit.plugin.Plugin; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import oshi.SystemInfo; - -import java.io.*; -import java.lang.annotation.Annotation; -import java.math.RoundingMode; -import java.net.URL; -import java.text.NumberFormat; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import static com.volmit.iris.core.safeguard.IrisSafeguard.*; -import static com.volmit.iris.core.safeguard.ServerBootSFG.passedserversoftware; - -@SuppressWarnings("CanBeFinal") -public class Iris extends VolmitPlugin implements Listener { - private static final Queue syncJobs = new ShurikenQueue<>(); - - public static Iris instance; - public static BukkitAudiences audiences; - public static MultiverseCoreLink linkMultiverseCore; - public static MythicMobsLink linkMythicMobs; - public static IrisCompat compat; - public static FileWatcher configWatcher; - private static VolmitSender sender; - - static { - try { - fixShading(); - InstanceState.updateInstanceId(); - } catch (Throwable ignored) { - - } - } - - private final KList postShutdown = new KList<>(); - private KMap, IrisService> services; - - public static VolmitSender getSender() { - if (sender == null) { - sender = new VolmitSender(Bukkit.getConsoleSender()); - sender.setTag(instance.getTag()); - } - return sender; - } - - @SuppressWarnings("unchecked") - public static T service(Class c) { - return (T) instance.services.get(c); - } - - public static void callEvent(Event e) { - if (!e.isAsynchronous()) { - J.s(() -> Bukkit.getPluginManager().callEvent(e)); - } else { - Bukkit.getPluginManager().callEvent(e); - } - } - - public static KList initialize(String s, Class slicedClass) { - JarScanner js = new JarScanner(instance.getJarFile(), s); - KList v = new KList<>(); - J.attempt(js::scan); - for (Class i : js.getClasses()) { - if (slicedClass == null || i.isAnnotationPresent(slicedClass)) { - try { - v.add(i.getDeclaredConstructor().newInstance()); - } catch (Throwable ignored) { - - } - } - } - - return v; - } - - public static KList> getClasses(String s, Class slicedClass) { - JarScanner js = new JarScanner(instance.getJarFile(), s); - KList> v = new KList<>(); - J.attempt(js::scan); - for (Class i : js.getClasses()) { - if (slicedClass == null || i.isAnnotationPresent(slicedClass)) { - try { - v.add(i); - } catch (Throwable ignored) { - - } - } - } - - return v; - } - - public static KList initialize(String s) { - return initialize(s, null); - } - - public static void sq(Runnable r) { - synchronized (syncJobs) { - syncJobs.queue(r); - } - } - - public static File getTemp() { - return instance.getDataFolder("cache", "temp"); - } - - public static void msg(String string) { - try { - getSender().sendMessage(string); - } catch (Throwable e) { - try { - instance.getLogger().info(instance.getTag() + string.replaceAll("(<([^>]+)>)", "")); - } catch (Throwable ignored1) { - - } - } - } - - public static File getCached(String name, String url) { - String h = IO.hash(name + "@" + url); - File f = Iris.instance.getDataFile("cache", h.substring(0, 2), h.substring(3, 5), h); - - if (!f.exists()) { - try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) { - byte[] dataBuffer = new byte[1024]; - int bytesRead; - while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { - fileOutputStream.write(dataBuffer, 0, bytesRead); - Iris.verbose("Aquiring " + name); - } - } catch (IOException e) { - Iris.reportError(e); - } - } - - return f.exists() ? f : null; - } - - public static String getNonCached(String name, String url) { - String h = IO.hash(name + "*" + url); - File f = Iris.instance.getDataFile("cache", h.substring(0, 2), h.substring(3, 5), h); - - try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) { - byte[] dataBuffer = new byte[1024]; - int bytesRead; - while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { - fileOutputStream.write(dataBuffer, 0, bytesRead); - } - } catch (IOException e) { - Iris.reportError(e); - } - - try { - return IO.readAll(f); - } catch (IOException e) { - Iris.reportError(e); - } - - return ""; - } - - public static File getNonCachedFile(String name, String url) { - String h = IO.hash(name + "*" + url); - File f = Iris.instance.getDataFile("cache", h.substring(0, 2), h.substring(3, 5), h); - Iris.verbose("Download " + name + " -> " + url); - try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) { - byte[] dataBuffer = new byte[1024]; - int bytesRead; - while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { - fileOutputStream.write(dataBuffer, 0, bytesRead); - } - - fileOutputStream.flush(); - } catch (IOException e) { - e.printStackTrace(); - Iris.reportError(e); - } - - return f; - } - - public static void warn(String format, Object... objs) { - msg(C.YELLOW + String.format(format, objs)); - } - - public static void error(String format, Object... objs) { - msg(C.RED + String.format(format, objs)); - } - - public static void debug(String string) { - if (!IrisSettings.get().getGeneral().isDebug()) { - return; - } - - try { - throw new RuntimeException(); - } catch (Throwable e) { - try { - String[] cc = e.getStackTrace()[1].getClassName().split("\\Q.\\E"); - - if (cc.length > 5) { - debug(cc[3] + "/" + cc[4] + "/" + cc[cc.length - 1], e.getStackTrace()[1].getLineNumber(), string); - } else { - debug(cc[3] + "/" + cc[4], e.getStackTrace()[1].getLineNumber(), string); - } - } catch (Throwable ex) { - debug("Origin", -1, string); - } - } - } - - public static void debug(String category, int line, String string) { - if (!IrisSettings.get().getGeneral().isDebug()) { - return; - } - if (IrisSettings.get().getGeneral().isUseConsoleCustomColors()) { - msg("" + category + " <#bf3b76>" + line + " " + C.LIGHT_PURPLE + string.replaceAll("\\Q<\\E", "[").replaceAll("\\Q>\\E", "]")); - } else { - msg(C.BLUE + category + ":" + C.AQUA + line + C.RESET + C.LIGHT_PURPLE + " " + string.replaceAll("\\Q<\\E", "[").replaceAll("\\Q>\\E", "]")); - - } - } - - public static void verbose(String string) { - debug(string); - } - - public static void success(String string) { - msg(C.IRIS + string); - } - - public static void info(String format, Object... args) { - msg(C.WHITE + String.format(format, args)); - } - public static void safeguard(String format, Object... args) { - msg(C.RESET + String.format(format, args)); - } - - @SuppressWarnings("deprecation") - public static void later(NastyRunnable object) { - try { - Bukkit.getScheduler().scheduleAsyncDelayedTask(instance, () -> - { - try { - object.run(); - } catch (Throwable e) { - e.printStackTrace(); - Iris.reportError(e); - } - }, RNG.r.i(100, 1200)); - } catch (IllegalPluginAccessException ignored) { - - } - } - - public static int jobCount() { - return syncJobs.size(); - } - - public static void clearQueues() { - synchronized (syncJobs) { - syncJobs.clear(); - } - } - - public static int getJavaVersion() { - String version = System.getProperty("java.version"); - if (version.startsWith("1.")) { - version = version.substring(2, 3); - } else { - int dot = version.indexOf("."); - if (dot != -1) { - version = version.substring(0, dot); - } - } - return Integer.parseInt(version); - } - - public static String getJava() { - String javaRuntimeName = System.getProperty("java.vm.name"); - String javaRuntimeVendor = System.getProperty("java.vendor"); - String javaRuntimeVersion = System.getProperty("java.vm.version"); - return String.format("%s %s (build %s)", javaRuntimeName, javaRuntimeVendor, javaRuntimeVersion); - } - - public static void reportErrorChunk(int x, int z, Throwable e, String extra) { - if (IrisSettings.get().getGeneral().isDebug()) { - File f = instance.getDataFile("debug", "chunk-errors", "chunk." + x + "." + z + ".txt"); - - if (!f.exists()) { - J.attempt(() -> { - PrintWriter pw = new PrintWriter(f); - pw.println("Thread: " + Thread.currentThread().getName()); - pw.println("First: " + new Date(M.ms())); - e.printStackTrace(pw); - pw.close(); - }); - } - - Iris.debug("Chunk " + x + "," + z + " Exception Logged: " + e.getClass().getSimpleName() + ": " + C.RESET + "" + C.LIGHT_PURPLE + e.getMessage()); - } - } - - public static void reportError(Throwable e) { - Sentry.captureException(e); - if (IrisSettings.get().getGeneral().isDebug()) { - String n = e.getClass().getCanonicalName() + "-" + e.getStackTrace()[0].getClassName() + "-" + e.getStackTrace()[0].getLineNumber(); - - if (e.getCause() != null) { - n += "-" + e.getCause().getStackTrace()[0].getClassName() + "-" + e.getCause().getStackTrace()[0].getLineNumber(); - } - - File f = instance.getDataFile("debug", "caught-exceptions", n + ".txt"); - - if (!f.exists()) { - J.attempt(() -> { - PrintWriter pw = new PrintWriter(f); - pw.println("Thread: " + Thread.currentThread().getName()); - pw.println("First: " + new Date(M.ms())); - e.printStackTrace(pw); - pw.close(); - }); - } - - Iris.debug("Exception Logged: " + e.getClass().getSimpleName() + ": " + C.RESET + "" + C.LIGHT_PURPLE + e.getMessage()); - } - } - - public static void dump() { - try { - File fi = Iris.instance.getDataFile("dump", "td-" + new java.sql.Date(M.ms()) + ".txt"); - FileOutputStream fos = new FileOutputStream(fi); - Map f = Thread.getAllStackTraces(); - PrintWriter pw = new PrintWriter(fos); - for (Thread i : f.keySet()) { - pw.println("========================================"); - pw.println("Thread: '" + i.getName() + "' ID: " + i.getId() + " STATUS: " + i.getState().name()); - - for (StackTraceElement j : f.get(i)) { - pw.println(" @ " + j.toString()); - } - - pw.println("========================================"); - pw.println(); - pw.println(); - } - - pw.close(); - Iris.info("DUMPED! See " + fi.getAbsolutePath()); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void panic() { - EnginePanic.panic(); - } - - public static void addPanic(String s, String v) { - EnginePanic.add(s, v); - } - - private static void fixShading() { - ShadeFix.fix(ComponentSerializer.class); - } - private void enable() { - instance = this; - services = new KMap<>(); - setupAudience(); - setupSentry(); - initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class) i.getClass(), (IrisService) i)); - INMS.get(); - IO.delete(new File("iris")); - compat = IrisCompat.configured(getDataFile("compat.json")); - ServerConfigurator.configure(); - new IrisContextInjector(); - IrisSafeguard.IrisSafeguardSystem(); - getSender().setTag(getTag()); - IrisSafeguard.earlySplash(); - linkMultiverseCore = new MultiverseCoreLink(); - linkMythicMobs = new MythicMobsLink(); - configWatcher = new FileWatcher(getDataFile("settings.json")); - services.values().forEach(IrisService::onEnable); - services.values().forEach(this::registerListener); - J.s(() -> { - J.a(() -> PaperLib.suggestPaper(this)); - J.a(() -> IO.delete(getTemp())); - J.a(LazyPregenerator::loadLazyGenerators, 100); - J.a(this::bstats); - J.ar(this::checkConfigHotload, 60); - J.sr(this::tickQueue, 0); - J.s(this::setupPapi); - J.a(ServerConfigurator::configure, 20); - splash(); - UtilsSFG.splash(); - - autoStartStudio(); - checkForBukkitWorlds(); - IrisToolbelt.retainMantleDataForSlice(String.class.getCanonicalName()); - IrisToolbelt.retainMantleDataForSlice(BlockData.class.getCanonicalName()); - }); - } - - private void checkForBukkitWorlds() { - FileConfiguration fc = new YamlConfiguration(); - try { - fc.load(new File("bukkit.yml")); - ConfigurationSection section = fc.getConfigurationSection("worlds"); - if (section == null) { - return; - } - - for (String s : section.getKeys(false)) { - ConfigurationSection entry = section.getConfigurationSection(s); - if (!entry.contains("generator", true)) { - continue; - } - - String generator = entry.getString("generator"); - if (generator.startsWith("Iris:")) { - generator = generator.split("\\Q:\\E")[1]; - } else if (generator.equalsIgnoreCase("Iris")) { - generator = IrisSettings.get().getGenerator().getDefaultWorldType(); - } else { - continue; - } - - if (Bukkit.getWorld(s) != null) - continue; - - Iris.info("Loading World: %s | Generator: %s", s, generator); - - Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "..."); - WorldCreator c = new WorldCreator(s) - .generator(getDefaultWorldGenerator(s, generator)) - .environment(IrisData.loadAnyDimension(generator).getEnvironment()); - INMS.get().createWorld(c); - Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!"); - } - } catch (Throwable e) { - e.printStackTrace(); - reportError(e); - } - } - - private void autoStartStudio() { - if (IrisSettings.get().getStudio().isAutoStartDefaultStudio()) { - Iris.info("Starting up auto Studio!"); - try { - Player r = new KList<>(getServer().getOnlinePlayers()).getRandom(); - Iris.service(StudioSVC.class).open(r != null ? new VolmitSender(r) : getSender(), 1337, IrisSettings.get().getGenerator().getDefaultWorldType(), (w) -> { - J.s(() -> { - for (Player i : getServer().getOnlinePlayers()) { - i.setGameMode(GameMode.SPECTATOR); - i.teleport(new Location(w, 0, 200, 0)); - } - }); - }); - } catch (IrisException e) { - reportError(e); - } - } - } - - private void setupAudience() { - try { - audiences = BukkitAudiences.create(this); - } catch (Throwable e) { - e.printStackTrace(); - IrisSettings.get().getGeneral().setUseConsoleCustomColors(false); - IrisSettings.get().getGeneral().setUseCustomColorsIngame(false); - Iris.error("Failed to setup Adventure API... No custom colors :("); - } - } - - public void postShutdown(Runnable r) { - postShutdown.add(r); - } - - public void onEnable() { - enable(); - super.onEnable(); - Bukkit.getPluginManager().registerEvents(this, this); - setupChecks(); - } - - public void onDisable() { - services.values().forEach(IrisService::onDisable); - Bukkit.getScheduler().cancelTasks(this); - HandlerList.unregisterAll((Plugin) this); - postShutdown.forEach(Runnable::run); - super.onDisable(); - - J.attempt(new JarScanner(instance.getJarFile(), "", false)::scan); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - Bukkit.getWorlds() - .stream() - .map(IrisToolbelt::access) - .filter(Objects::nonNull) - .forEach(PlatformChunkGenerator::close); - - MultiBurst.burst.close(); - services.clear(); - })); - } - - private void setupPapi() { - if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { - new IrisPapiExpansion().register(); - } - } - - @Override - public void start() { - - } - - @Override - public void stop() { - - } - - @Override - public String getTag(String subTag) { - if (unstablemode) { - return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.RED + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; - } - if (warningmode) { - return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.GOLD + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; - } - return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.IRIS + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; - - } - - private boolean setupChecks() { - boolean passed = true; - Iris.info("Version Information: " + instance.getServer().getVersion() + " | " + instance.getServer().getBukkitVersion()); - if (INMS.get() instanceof NMSBinding1X) { - passed = false; - Iris.warn("============================================"); - Iris.warn("="); - Iris.warn("="); - Iris.warn("="); - Iris.warn("Iris is not compatible with this version of Minecraft."); - Iris.warn("="); - Iris.warn("="); - Iris.warn("="); - Iris.warn("============================================"); - } - - try { - Class.forName("io.papermc.paper.configuration.PaperConfigurations"); - } catch (ClassNotFoundException e) { - Iris.info(C.RED + "Iris requires paper or above to function properly.."); - return false; - } - - try { - Class.forName("org.purpurmc.purpur.PurpurConfig"); - } catch (ClassNotFoundException e) { - Iris.info("We recommend using Purpur for the best experience with Iris."); - Iris.info("Purpur is a fork of Paper that is optimized for performance and stability."); - Iris.info("Plugins that work on Spigot / Paper work on Purpur."); - Iris.info("You can download it here: https://purpurmc.org"); - return false; - } - return passed; - } - - private void checkConfigHotload() { - if (configWatcher.checkModified()) { - IrisSettings.invalidate(); - IrisSettings.get(); - configWatcher.checkModified(); - Iris.info("Hotloaded settings.json "); - } - } - - private void tickQueue() { - synchronized (Iris.syncJobs) { - if (!Iris.syncJobs.hasNext()) { - return; - } - - long ms = M.ms(); - - while (Iris.syncJobs.hasNext() && M.ms() - ms < 25) { - try { - Iris.syncJobs.next().run(); - } catch (Throwable e) { - e.printStackTrace(); - Iris.reportError(e); - } - } - } - } - - private void bstats() { - if (IrisSettings.get().getGeneral().isPluginMetrics()) { - J.s(() -> { - var metrics = new Metrics(Iris.instance, 24220); - metrics.addCustomChart(new SingleLineChart("custom_dimensions", () -> Bukkit.getWorlds() - .stream() - .filter(IrisToolbelt::isIrisWorld) - .mapToInt(w -> 1) - .sum())); - - metrics.addCustomChart(new DrilldownPie("used_packs", () -> Bukkit.getWorlds().stream() - .map(IrisToolbelt::access) - .filter(Objects::nonNull) - .map(PlatformChunkGenerator::getEngine) - .collect(Collectors.toMap(engine -> engine.getDimension().getLoadKey(), engine -> { - var hash32 = engine.getHash32().getNow(null); - if (hash32 == null) return Map.of(); - int version = engine.getDimension().getVersion(); - String checksum = Long.toHexString(hash32); - - return Map.of("v" + version + " (" + checksum + ")", 1); - }, (a, b) -> { - Map merged = new HashMap<>(a); - b.forEach((k, v) -> merged.merge(k, v, Integer::sum)); - return merged; - })))); - - - var info = new SystemInfo().getHardware(); - var cpu = info.getProcessor().getProcessorIdentifier(); - var mem = info.getMemory(); - metrics.addCustomChart(new SimplePie("cpu_model", cpu::getName)); - - var nf = NumberFormat.getInstance(Locale.ENGLISH); - nf.setMinimumFractionDigits(0); - nf.setMaximumFractionDigits(2); - nf.setRoundingMode(RoundingMode.HALF_UP); - - metrics.addCustomChart(new DrilldownPie("memory", () -> { - double total = mem.getTotal() * 1E-9; - double alloc = Math.min(total, Runtime.getRuntime().maxMemory() * 1E-9); - return Map.of(nf.format(alloc), Map.of(nf.format(total), 1)); - })); - - postShutdown.add(metrics::shutdown); - }); - } - } - - @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - return super.onCommand(sender, command, label, args); - } - - public void imsg(CommandSender s, String msg) { - s.sendMessage(C.IRIS + "[" + C.DARK_GRAY + "Iris" + C.IRIS + "]" + C.GRAY + ": " + msg); - } - - @Nullable - @Override - public BiomeProvider getDefaultBiomeProvider(@NotNull String worldName, @Nullable String id) { - Iris.debug("Biome Provider Called for " + worldName + " using ID: " + id); - return super.getDefaultBiomeProvider(worldName, id); - } - - @Override - public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { - Iris.debug("Default World Generator Called for " + worldName + " using ID: " + id); - if (worldName.equals("test")) { - try { - throw new RuntimeException(); - } catch (Throwable e) { - Iris.info(e.getStackTrace()[1].getClassName()); - if (e.getStackTrace()[1].getClassName().contains("com.onarandombox.MultiverseCore")) { - Iris.debug("MVC Test detected, Quick! Send them the dummy!"); - return new DummyChunkGenerator(); - } - } - } - - IrisDimension dim; - if (id == null || id.isEmpty()) { - dim = IrisData.loadAnyDimension(IrisSettings.get().getGenerator().getDefaultWorldType()); - } else { - dim = IrisData.loadAnyDimension(id); - } - Iris.debug("Generator ID: " + id + " requested by bukkit/plugin"); - - if (dim == null) { - Iris.warn("Unable to find dimension type " + id + " Looking for online packs..."); - - service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, true); - dim = IrisData.loadAnyDimension(id); - - if (dim == null) { - throw new RuntimeException("Can't find dimension " + id + "!"); - } else { - Iris.info("Resolved missing dimension, proceeding with generation."); - } - } - - Iris.debug("Assuming IrisDimension: " + dim.getName()); - - IrisWorld w = IrisWorld.builder() - .name(worldName) - .seed(1337) - .environment(dim.getEnvironment()) - .worldFolder(new File(Bukkit.getWorldContainer(), worldName)) - .minHeight(dim.getMinHeight()) - .maxHeight(dim.getMaxHeight()) - .build(); - - Iris.debug("Generator Config: " + w.toString()); - - File ff = new File(w.worldFolder(), "iris/pack"); - if (!ff.exists() || ff.listFiles().length == 0) { - ff.mkdirs(); - service(StudioSVC.class).installIntoWorld(getSender(), dim.getLoadKey(), w.worldFolder()); - } - - return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey()); - } - - public void splash() { - if (!IrisSettings.get().getGeneral().isSplashLogoStartup()) { - return; - } - - String padd = Form.repeat(" ", 8); - String padd2 = Form.repeat(" ", 4); - String[] info = {"", "", "", "", "", padd2 + C.IRIS + " Iris", padd2 + C.GRAY + " by " + "Volmit Software", padd2 + C.GRAY + " v" + C.IRIS + getDescription().getVersion()}; - if (unstablemode) { - info = new String[]{"", "", "", "", "", padd2 + C.RED + " Iris", padd2 + C.GRAY + " by " + C.DARK_RED + "Volmit Software", padd2 + C.GRAY + " v" + C.RED + getDescription().getVersion()}; - } - if (warningmode) { - info = new String[]{"", "", "", "", "", padd2 + C.GOLD + " Iris", padd2 + C.GRAY + " by " + C.GOLD + "Volmit Software", padd2 + C.GRAY + " v" + C.GOLD + getDescription().getVersion()}; - } - - String[] splashstable = { - padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@", - padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + C.IRIS + " .(((()))). ", - padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + C.IRIS + " .((((((())))))). ", - padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + C.IRIS + " ((((((((())))))))) " + C.GRAY + " @", - padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + C.IRIS + " ((((((((-))))))))) " + C.GRAY + " @@", - padd + C.GRAY + "@@@&&" + C.IRIS + " ((((((({ })))))))) " + C.GRAY + " &&@@@", - padd + C.GRAY + "@@" + C.IRIS + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@", - padd + C.GRAY + "@" + C.IRIS + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@", - padd + C.GRAY + "" + C.IRIS + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@", - padd + C.GRAY + "" + C.IRIS + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@", - padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@" - }; - - String[] splashunstable = { - padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@", - padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + C.RED + " .(((()))). ", - padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + C.RED + " .((((((())))))). ", - padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + C.RED + " ((((((((())))))))) " + C.GRAY + " @", - padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + C.RED + " ((((((((-))))))))) " + C.GRAY + " @@", - padd + C.GRAY + "@@@&&" + C.RED + " ((((((({ })))))))) " + C.GRAY + " &&@@@", - padd + C.GRAY + "@@" + C.RED + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@", - padd + C.GRAY + "@" + C.RED + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@", - padd + C.GRAY + "" + C.RED + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@", - padd + C.GRAY + "" + C.RED + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@", - padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@" - }; - String[] splashwarning = { - padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@", - padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + C.GOLD + " .(((()))). ", - padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + C.GOLD + " .((((((())))))). ", - padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + C.GOLD + " ((((((((())))))))) " + C.GRAY + " @", - padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + C.GOLD + " ((((((((-))))))))) " + C.GRAY + " @@", - padd + C.GRAY + "@@@&&" + C.GOLD + " ((((((({ })))))))) " + C.GRAY + " &&@@@", - padd + C.GRAY + "@@" + C.GOLD + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@", - padd + C.GRAY + "@" + C.GOLD + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@", - padd + C.GRAY + "" + C.GOLD + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@", - padd + C.GRAY + "" + C.GOLD + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@", - padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@" - }; - String[] splash; - File freeSpace = new File(Bukkit.getWorldContainer() + "."); - if (unstablemode) { - splash = splashunstable; - } else if (warningmode) { - splash = splashwarning; - } else { - splash = splashstable; - } - - if (!passedserversoftware) { - Iris.info("Server type & version: " + C.RED + Bukkit.getVersion()); - } else { Iris.info("Server type & version: " + Bukkit.getVersion()); } - Iris.info("Java: " + getJava()); - if (getHardware.getProcessMemory() < 5999) { - Iris.warn("6GB+ Ram is recommended"); - Iris.warn("Process Memory: " + getHardware.getProcessMemory() + " MB"); - } - Iris.info("Bukkit distro: " + Bukkit.getName()); - Iris.info("Custom Biomes: " + INMS.get().countCustomBiomes()); - setupChecks(); - printPacks(); - - for (int i = 0; i < info.length; i++) { - splash[i] += info[i]; - } - - Iris.info("\n\n " + new KList<>(splash).toString("\n") + "\n"); - } - - private void printPacks() { - File packFolder = Iris.service(StudioSVC.class).getWorkspaceFolder(); - File[] packs = packFolder.listFiles(File::isDirectory); - if (packs == null || packs.length == 0) - return; - Iris.info("Custom Dimensions: " + packs.length); - for (File f : packs) - printPack(f); - } - - private void printPack(File pack) { - String dimName = pack.getName(); - String version = "???"; - try (FileReader r = new FileReader(new File(pack, "dimensions/" + dimName + ".json"))) { - JsonObject json = JsonParser.parseReader(r).getAsJsonObject(); - if (json.has("version")) - version = json.get("version").getAsString(); - } catch (IOException | JsonParseException ignored) { - } - Iris.info(" " + dimName + " v" + version); - } - - public int getIrisVersion() { - String input = Iris.instance.getDescription().getVersion(); - int hyphenIndex = input.indexOf('-'); - if (hyphenIndex != -1) { - String result = input.substring(0, hyphenIndex); - result = result.replaceAll("\\.", ""); - return Integer.parseInt(result); - } - return -1; - } - - public int getMCVersion() { - try { - String version = Bukkit.getVersion(); - Matcher matcher = Pattern.compile("\\(MC: ([\\d.]+)\\)").matcher(version); - if (matcher.find()) { - version = matcher.group(1).replaceAll("\\.", ""); - long versionNumber = Long.parseLong(version); - if (versionNumber > Integer.MAX_VALUE) { - return -1; - } - return (int) versionNumber; - } - return -1; - } catch (Exception e) { - return -1; - } - } - - private static boolean suppress(Throwable e) { - return (e instanceof IllegalStateException ex && "zip file closed".equals(ex.getMessage())) || e instanceof JSONException; - } - - private static void setupSentry() { - var settings = IrisSettings.get().getSentry(); - if (settings.disableAutoReporting || Sentry.isEnabled()) return; - Iris.info("Enabling Sentry for anonymous error reporting. You can disable this in the settings."); - Sentry.init(options -> { - options.setDsn("https://b16ecc222e9c1e0c48faecacb906fd89@o4509451052646400.ingest.de.sentry.io/4509452722765904"); - if (settings.debug) { - options.setLogger(new IrisLogger()); - options.setDebug(true); - } - - options.setAttachServerName(false); - options.setEnableUncaughtExceptionHandler(false); - options.setRelease(Iris.instance.getDescription().getVersion()); - options.setBeforeSend((event, hint) -> { - if (suppress(event.getThrowable())) return null; - event.setTag("iris.safeguard", IrisSafeguard.mode()); - event.setTag("iris.nms", INMS.get().getClass().getCanonicalName()); - var context = IrisContext.get(); - if (context != null) event.getContexts().set("engine", context.asContext()); - return event; - }); - }); - Sentry.configureScope(scope -> { - scope.addAttachment(Attachments.PLUGINS); - scope.setTag("server", Bukkit.getVersion()); - scope.setTag("server.type", Bukkit.getName()); - scope.setTag("server.api", Bukkit.getBukkitVersion()); - }); - Runtime.getRuntime().addShutdownHook(new Thread(Sentry::close)); - } -} +/* + * 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 com.volmit.iris; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.ServerConfigurator; +import com.volmit.iris.core.link.IrisPapiExpansion; +import com.volmit.iris.core.link.MultiverseCoreLink; +import com.volmit.iris.core.link.MythicMobsLink; +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.core.nms.v1X.NMSBinding1X; +import com.volmit.iris.core.pregenerator.LazyPregenerator; +import com.volmit.iris.core.service.StudioSVC; +import com.volmit.iris.core.tools.IrisToolbelt; +import com.volmit.iris.engine.EnginePanic; +import com.volmit.iris.engine.object.IrisCompat; +import com.volmit.iris.engine.object.IrisContextInjector; +import com.volmit.iris.engine.object.IrisDimension; +import com.volmit.iris.engine.object.IrisWorld; +import com.volmit.iris.engine.platform.BukkitChunkGenerator; +import com.volmit.iris.engine.platform.DummyChunkGenerator; +import com.volmit.iris.core.safeguard.IrisSafeguard; +import com.volmit.iris.core.safeguard.UtilsSFG; +import com.volmit.iris.engine.platform.PlatformChunkGenerator; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.context.IrisContext; +import com.volmit.iris.util.exceptions.IrisException; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.function.NastyRunnable; +import com.volmit.iris.util.io.FileWatcher; +import com.volmit.iris.util.io.IO; +import com.volmit.iris.util.io.InstanceState; +import com.volmit.iris.util.io.JarScanner; +import com.volmit.iris.util.json.JSONException; +import com.volmit.iris.util.math.M; +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.misc.getHardware; +import com.volmit.iris.util.parallel.MultiBurst; +import com.volmit.iris.util.plugin.IrisService; +import com.volmit.iris.util.plugin.VolmitPlugin; +import com.volmit.iris.util.plugin.VolmitSender; +import com.volmit.iris.util.reflect.ShadeFix; +import com.volmit.iris.util.scheduling.J; +import com.volmit.iris.util.scheduling.Queue; +import com.volmit.iris.util.scheduling.ShurikenQueue; +import com.volmit.iris.util.sentry.Attachments; +import com.volmit.iris.util.sentry.IrisLogger; +import com.volmit.iris.util.sentry.ServerID; +import io.papermc.lib.PaperLib; +import io.sentry.Sentry; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import org.bstats.bukkit.Metrics; +import org.bstats.charts.DrilldownPie; +import org.bstats.charts.SimplePie; +import org.bstats.charts.SingleLineChart; +import org.bukkit.*; +import org.bukkit.block.data.BlockData; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.*; +import org.bukkit.generator.BiomeProvider; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.IllegalPluginAccessException; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import oshi.SystemInfo; + +import java.io.*; +import java.lang.annotation.Annotation; +import java.math.RoundingMode; +import java.net.URL; +import java.text.NumberFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static com.volmit.iris.core.safeguard.IrisSafeguard.*; +import static com.volmit.iris.core.safeguard.ServerBootSFG.passedserversoftware; + +@SuppressWarnings("CanBeFinal") +public class Iris extends VolmitPlugin implements Listener { + private static final Queue syncJobs = new ShurikenQueue<>(); + + public static Iris instance; + public static BukkitAudiences audiences; + public static MultiverseCoreLink linkMultiverseCore; + public static MythicMobsLink linkMythicMobs; + public static IrisCompat compat; + public static FileWatcher configWatcher; + private static VolmitSender sender; + + static { + try { + fixShading(); + InstanceState.updateInstanceId(); + } catch (Throwable ignored) { + + } + } + + private final KList postShutdown = new KList<>(); + private KMap, IrisService> services; + + public static VolmitSender getSender() { + if (sender == null) { + sender = new VolmitSender(Bukkit.getConsoleSender()); + sender.setTag(instance.getTag()); + } + return sender; + } + + @SuppressWarnings("unchecked") + public static T service(Class c) { + return (T) instance.services.get(c); + } + + public static void callEvent(Event e) { + if (!e.isAsynchronous()) { + J.s(() -> Bukkit.getPluginManager().callEvent(e)); + } else { + Bukkit.getPluginManager().callEvent(e); + } + } + + public static KList initialize(String s, Class slicedClass) { + JarScanner js = new JarScanner(instance.getJarFile(), s); + KList v = new KList<>(); + J.attempt(js::scan); + for (Class i : js.getClasses()) { + if (slicedClass == null || i.isAnnotationPresent(slicedClass)) { + try { + v.add(i.getDeclaredConstructor().newInstance()); + } catch (Throwable ignored) { + + } + } + } + + return v; + } + + public static KList> getClasses(String s, Class slicedClass) { + JarScanner js = new JarScanner(instance.getJarFile(), s); + KList> v = new KList<>(); + J.attempt(js::scan); + for (Class i : js.getClasses()) { + if (slicedClass == null || i.isAnnotationPresent(slicedClass)) { + try { + v.add(i); + } catch (Throwable ignored) { + + } + } + } + + return v; + } + + public static KList initialize(String s) { + return initialize(s, null); + } + + public static void sq(Runnable r) { + synchronized (syncJobs) { + syncJobs.queue(r); + } + } + + public static File getTemp() { + return instance.getDataFolder("cache", "temp"); + } + + public static void msg(String string) { + try { + getSender().sendMessage(string); + } catch (Throwable e) { + try { + instance.getLogger().info(instance.getTag() + string.replaceAll("(<([^>]+)>)", "")); + } catch (Throwable ignored1) { + + } + } + } + + public static File getCached(String name, String url) { + String h = IO.hash(name + "@" + url); + File f = Iris.instance.getDataFile("cache", h.substring(0, 2), h.substring(3, 5), h); + + if (!f.exists()) { + try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) { + byte[] dataBuffer = new byte[1024]; + int bytesRead; + while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { + fileOutputStream.write(dataBuffer, 0, bytesRead); + Iris.verbose("Aquiring " + name); + } + } catch (IOException e) { + Iris.reportError(e); + } + } + + return f.exists() ? f : null; + } + + public static String getNonCached(String name, String url) { + String h = IO.hash(name + "*" + url); + File f = Iris.instance.getDataFile("cache", h.substring(0, 2), h.substring(3, 5), h); + + try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) { + byte[] dataBuffer = new byte[1024]; + int bytesRead; + while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { + fileOutputStream.write(dataBuffer, 0, bytesRead); + } + } catch (IOException e) { + Iris.reportError(e); + } + + try { + return IO.readAll(f); + } catch (IOException e) { + Iris.reportError(e); + } + + return ""; + } + + public static File getNonCachedFile(String name, String url) { + String h = IO.hash(name + "*" + url); + File f = Iris.instance.getDataFile("cache", h.substring(0, 2), h.substring(3, 5), h); + Iris.verbose("Download " + name + " -> " + url); + try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) { + byte[] dataBuffer = new byte[1024]; + int bytesRead; + while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { + fileOutputStream.write(dataBuffer, 0, bytesRead); + } + + fileOutputStream.flush(); + } catch (IOException e) { + e.printStackTrace(); + Iris.reportError(e); + } + + return f; + } + + public static void warn(String format, Object... objs) { + msg(C.YELLOW + String.format(format, objs)); + } + + public static void error(String format, Object... objs) { + msg(C.RED + String.format(format, objs)); + } + + public static void debug(String string) { + if (!IrisSettings.get().getGeneral().isDebug()) { + return; + } + + try { + throw new RuntimeException(); + } catch (Throwable e) { + try { + String[] cc = e.getStackTrace()[1].getClassName().split("\\Q.\\E"); + + if (cc.length > 5) { + debug(cc[3] + "/" + cc[4] + "/" + cc[cc.length - 1], e.getStackTrace()[1].getLineNumber(), string); + } else { + debug(cc[3] + "/" + cc[4], e.getStackTrace()[1].getLineNumber(), string); + } + } catch (Throwable ex) { + debug("Origin", -1, string); + } + } + } + + public static void debug(String category, int line, String string) { + if (!IrisSettings.get().getGeneral().isDebug()) { + return; + } + if (IrisSettings.get().getGeneral().isUseConsoleCustomColors()) { + msg("" + category + " <#bf3b76>" + line + " " + C.LIGHT_PURPLE + string.replaceAll("\\Q<\\E", "[").replaceAll("\\Q>\\E", "]")); + } else { + msg(C.BLUE + category + ":" + C.AQUA + line + C.RESET + C.LIGHT_PURPLE + " " + string.replaceAll("\\Q<\\E", "[").replaceAll("\\Q>\\E", "]")); + + } + } + + public static void verbose(String string) { + debug(string); + } + + public static void success(String string) { + msg(C.IRIS + string); + } + + public static void info(String format, Object... args) { + msg(C.WHITE + String.format(format, args)); + } + public static void safeguard(String format, Object... args) { + msg(C.RESET + String.format(format, args)); + } + + @SuppressWarnings("deprecation") + public static void later(NastyRunnable object) { + try { + Bukkit.getScheduler().scheduleAsyncDelayedTask(instance, () -> + { + try { + object.run(); + } catch (Throwable e) { + e.printStackTrace(); + Iris.reportError(e); + } + }, RNG.r.i(100, 1200)); + } catch (IllegalPluginAccessException ignored) { + + } + } + + public static int jobCount() { + return syncJobs.size(); + } + + public static void clearQueues() { + synchronized (syncJobs) { + syncJobs.clear(); + } + } + + public static int getJavaVersion() { + String version = System.getProperty("java.version"); + if (version.startsWith("1.")) { + version = version.substring(2, 3); + } else { + int dot = version.indexOf("."); + if (dot != -1) { + version = version.substring(0, dot); + } + } + return Integer.parseInt(version); + } + + public static String getJava() { + String javaRuntimeName = System.getProperty("java.vm.name"); + String javaRuntimeVendor = System.getProperty("java.vendor"); + String javaRuntimeVersion = System.getProperty("java.vm.version"); + return String.format("%s %s (build %s)", javaRuntimeName, javaRuntimeVendor, javaRuntimeVersion); + } + + public static void reportErrorChunk(int x, int z, Throwable e, String extra) { + if (IrisSettings.get().getGeneral().isDebug()) { + File f = instance.getDataFile("debug", "chunk-errors", "chunk." + x + "." + z + ".txt"); + + if (!f.exists()) { + J.attempt(() -> { + PrintWriter pw = new PrintWriter(f); + pw.println("Thread: " + Thread.currentThread().getName()); + pw.println("First: " + new Date(M.ms())); + e.printStackTrace(pw); + pw.close(); + }); + } + + Iris.debug("Chunk " + x + "," + z + " Exception Logged: " + e.getClass().getSimpleName() + ": " + C.RESET + "" + C.LIGHT_PURPLE + e.getMessage()); + } + } + + public static void reportError(Throwable e) { + Sentry.captureException(e); + if (IrisSettings.get().getGeneral().isDebug()) { + String n = e.getClass().getCanonicalName() + "-" + e.getStackTrace()[0].getClassName() + "-" + e.getStackTrace()[0].getLineNumber(); + + if (e.getCause() != null) { + n += "-" + e.getCause().getStackTrace()[0].getClassName() + "-" + e.getCause().getStackTrace()[0].getLineNumber(); + } + + File f = instance.getDataFile("debug", "caught-exceptions", n + ".txt"); + + if (!f.exists()) { + J.attempt(() -> { + PrintWriter pw = new PrintWriter(f); + pw.println("Thread: " + Thread.currentThread().getName()); + pw.println("First: " + new Date(M.ms())); + e.printStackTrace(pw); + pw.close(); + }); + } + + Iris.debug("Exception Logged: " + e.getClass().getSimpleName() + ": " + C.RESET + "" + C.LIGHT_PURPLE + e.getMessage()); + } + } + + public static void dump() { + try { + File fi = Iris.instance.getDataFile("dump", "td-" + new java.sql.Date(M.ms()) + ".txt"); + FileOutputStream fos = new FileOutputStream(fi); + Map f = Thread.getAllStackTraces(); + PrintWriter pw = new PrintWriter(fos); + for (Thread i : f.keySet()) { + pw.println("========================================"); + pw.println("Thread: '" + i.getName() + "' ID: " + i.getId() + " STATUS: " + i.getState().name()); + + for (StackTraceElement j : f.get(i)) { + pw.println(" @ " + j.toString()); + } + + pw.println("========================================"); + pw.println(); + pw.println(); + } + + pw.close(); + Iris.info("DUMPED! See " + fi.getAbsolutePath()); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + public static void panic() { + EnginePanic.panic(); + } + + public static void addPanic(String s, String v) { + EnginePanic.add(s, v); + } + + private static void fixShading() { + ShadeFix.fix(ComponentSerializer.class); + } + private void enable() { + instance = this; + services = new KMap<>(); + setupAudience(); + setupSentry(); + initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class) i.getClass(), (IrisService) i)); + INMS.get(); + IO.delete(new File("iris")); + compat = IrisCompat.configured(getDataFile("compat.json")); + ServerConfigurator.configure(); + new IrisContextInjector(); + IrisSafeguard.IrisSafeguardSystem(); + getSender().setTag(getTag()); + IrisSafeguard.earlySplash(); + linkMultiverseCore = new MultiverseCoreLink(); + linkMythicMobs = new MythicMobsLink(); + configWatcher = new FileWatcher(getDataFile("settings.json")); + services.values().forEach(IrisService::onEnable); + services.values().forEach(this::registerListener); + J.s(() -> { + J.a(() -> PaperLib.suggestPaper(this)); + J.a(() -> IO.delete(getTemp())); + J.a(LazyPregenerator::loadLazyGenerators, 100); + J.a(this::bstats); + J.ar(this::checkConfigHotload, 60); + J.sr(this::tickQueue, 0); + J.s(this::setupPapi); + J.a(ServerConfigurator::configure, 20); + splash(); + UtilsSFG.splash(); + + autoStartStudio(); + checkForBukkitWorlds(); + IrisToolbelt.retainMantleDataForSlice(String.class.getCanonicalName()); + IrisToolbelt.retainMantleDataForSlice(BlockData.class.getCanonicalName()); + }); + } + + private void checkForBukkitWorlds() { + FileConfiguration fc = new YamlConfiguration(); + try { + fc.load(new File("bukkit.yml")); + ConfigurationSection section = fc.getConfigurationSection("worlds"); + if (section == null) { + return; + } + + for (String s : section.getKeys(false)) { + ConfigurationSection entry = section.getConfigurationSection(s); + if (!entry.contains("generator", true)) { + continue; + } + + String generator = entry.getString("generator"); + if (generator.startsWith("Iris:")) { + generator = generator.split("\\Q:\\E")[1]; + } else if (generator.equalsIgnoreCase("Iris")) { + generator = IrisSettings.get().getGenerator().getDefaultWorldType(); + } else { + continue; + } + + if (Bukkit.getWorld(s) != null) + continue; + + Iris.info("Loading World: %s | Generator: %s", s, generator); + + Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "..."); + WorldCreator c = new WorldCreator(s) + .generator(getDefaultWorldGenerator(s, generator)) + .environment(IrisData.loadAnyDimension(generator).getEnvironment()); + INMS.get().createWorld(c); + Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!"); + } + } catch (Throwable e) { + e.printStackTrace(); + reportError(e); + } + } + + private void autoStartStudio() { + if (IrisSettings.get().getStudio().isAutoStartDefaultStudio()) { + Iris.info("Starting up auto Studio!"); + try { + Player r = new KList<>(getServer().getOnlinePlayers()).getRandom(); + Iris.service(StudioSVC.class).open(r != null ? new VolmitSender(r) : getSender(), 1337, IrisSettings.get().getGenerator().getDefaultWorldType(), (w) -> { + J.s(() -> { + for (Player i : getServer().getOnlinePlayers()) { + i.setGameMode(GameMode.SPECTATOR); + i.teleport(new Location(w, 0, 200, 0)); + } + }); + }); + } catch (IrisException e) { + reportError(e); + } + } + } + + private void setupAudience() { + try { + audiences = BukkitAudiences.create(this); + } catch (Throwable e) { + e.printStackTrace(); + IrisSettings.get().getGeneral().setUseConsoleCustomColors(false); + IrisSettings.get().getGeneral().setUseCustomColorsIngame(false); + Iris.error("Failed to setup Adventure API... No custom colors :("); + } + } + + public void postShutdown(Runnable r) { + postShutdown.add(r); + } + + public void onEnable() { + enable(); + super.onEnable(); + Bukkit.getPluginManager().registerEvents(this, this); + setupChecks(); + } + + public void onDisable() { + services.values().forEach(IrisService::onDisable); + Bukkit.getScheduler().cancelTasks(this); + HandlerList.unregisterAll((Plugin) this); + postShutdown.forEach(Runnable::run); + super.onDisable(); + + J.attempt(new JarScanner(instance.getJarFile(), "", false)::scan); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + Bukkit.getWorlds() + .stream() + .map(IrisToolbelt::access) + .filter(Objects::nonNull) + .forEach(PlatformChunkGenerator::close); + + MultiBurst.burst.close(); + services.clear(); + })); + } + + private void setupPapi() { + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + new IrisPapiExpansion().register(); + } + } + + @Override + public void start() { + + } + + @Override + public void stop() { + + } + + @Override + public String getTag(String subTag) { + if (unstablemode) { + return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.RED + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; + } + if (warningmode) { + return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.GOLD + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; + } + return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.IRIS + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; + + } + + private boolean setupChecks() { + boolean passed = true; + Iris.info("Version Information: " + instance.getServer().getVersion() + " | " + instance.getServer().getBukkitVersion()); + if (INMS.get() instanceof NMSBinding1X) { + passed = false; + Iris.warn("============================================"); + Iris.warn("="); + Iris.warn("="); + Iris.warn("="); + Iris.warn("Iris is not compatible with this version of Minecraft."); + Iris.warn("="); + Iris.warn("="); + Iris.warn("="); + Iris.warn("============================================"); + } + + try { + Class.forName("io.papermc.paper.configuration.PaperConfigurations"); + } catch (ClassNotFoundException e) { + Iris.info(C.RED + "Iris requires paper or above to function properly.."); + return false; + } + + try { + Class.forName("org.purpurmc.purpur.PurpurConfig"); + } catch (ClassNotFoundException e) { + Iris.info("We recommend using Purpur for the best experience with Iris."); + Iris.info("Purpur is a fork of Paper that is optimized for performance and stability."); + Iris.info("Plugins that work on Spigot / Paper work on Purpur."); + Iris.info("You can download it here: https://purpurmc.org"); + return false; + } + return passed; + } + + private void checkConfigHotload() { + if (configWatcher.checkModified()) { + IrisSettings.invalidate(); + IrisSettings.get(); + configWatcher.checkModified(); + Iris.info("Hotloaded settings.json "); + } + } + + private void tickQueue() { + synchronized (Iris.syncJobs) { + if (!Iris.syncJobs.hasNext()) { + return; + } + + long ms = M.ms(); + + while (Iris.syncJobs.hasNext() && M.ms() - ms < 25) { + try { + Iris.syncJobs.next().run(); + } catch (Throwable e) { + e.printStackTrace(); + Iris.reportError(e); + } + } + } + } + + private void bstats() { + if (IrisSettings.get().getGeneral().isPluginMetrics()) { + J.s(() -> { + var metrics = new Metrics(Iris.instance, 24220); + metrics.addCustomChart(new SingleLineChart("custom_dimensions", () -> Bukkit.getWorlds() + .stream() + .filter(IrisToolbelt::isIrisWorld) + .mapToInt(w -> 1) + .sum())); + + metrics.addCustomChart(new DrilldownPie("used_packs", () -> Bukkit.getWorlds().stream() + .map(IrisToolbelt::access) + .filter(Objects::nonNull) + .map(PlatformChunkGenerator::getEngine) + .collect(Collectors.toMap(engine -> engine.getDimension().getLoadKey(), engine -> { + var hash32 = engine.getHash32().getNow(null); + if (hash32 == null) return Map.of(); + int version = engine.getDimension().getVersion(); + String checksum = Long.toHexString(hash32); + + return Map.of("v" + version + " (" + checksum + ")", 1); + }, (a, b) -> { + Map merged = new HashMap<>(a); + b.forEach((k, v) -> merged.merge(k, v, Integer::sum)); + return merged; + })))); + + + var info = new SystemInfo().getHardware(); + var cpu = info.getProcessor().getProcessorIdentifier(); + var mem = info.getMemory(); + metrics.addCustomChart(new SimplePie("cpu_model", cpu::getName)); + + var nf = NumberFormat.getInstance(Locale.ENGLISH); + nf.setMinimumFractionDigits(0); + nf.setMaximumFractionDigits(2); + nf.setRoundingMode(RoundingMode.HALF_UP); + + metrics.addCustomChart(new DrilldownPie("memory", () -> { + double total = mem.getTotal() * 1E-9; + double alloc = Math.min(total, Runtime.getRuntime().maxMemory() * 1E-9); + return Map.of(nf.format(alloc), Map.of(nf.format(total), 1)); + })); + + postShutdown.add(metrics::shutdown); + }); + } + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + return super.onCommand(sender, command, label, args); + } + + public void imsg(CommandSender s, String msg) { + s.sendMessage(C.IRIS + "[" + C.DARK_GRAY + "Iris" + C.IRIS + "]" + C.GRAY + ": " + msg); + } + + @Nullable + @Override + public BiomeProvider getDefaultBiomeProvider(@NotNull String worldName, @Nullable String id) { + Iris.debug("Biome Provider Called for " + worldName + " using ID: " + id); + return super.getDefaultBiomeProvider(worldName, id); + } + + @Override + public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { + Iris.debug("Default World Generator Called for " + worldName + " using ID: " + id); + if (worldName.equals("test")) { + try { + throw new RuntimeException(); + } catch (Throwable e) { + Iris.info(e.getStackTrace()[1].getClassName()); + if (e.getStackTrace()[1].getClassName().contains("com.onarandombox.MultiverseCore")) { + Iris.debug("MVC Test detected, Quick! Send them the dummy!"); + return new DummyChunkGenerator(); + } + } + } + + IrisDimension dim; + if (id == null || id.isEmpty()) { + dim = IrisData.loadAnyDimension(IrisSettings.get().getGenerator().getDefaultWorldType()); + } else { + dim = IrisData.loadAnyDimension(id); + } + Iris.debug("Generator ID: " + id + " requested by bukkit/plugin"); + + if (dim == null) { + Iris.warn("Unable to find dimension type " + id + " Looking for online packs..."); + + service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, true); + dim = IrisData.loadAnyDimension(id); + + if (dim == null) { + throw new RuntimeException("Can't find dimension " + id + "!"); + } else { + Iris.info("Resolved missing dimension, proceeding with generation."); + } + } + + Iris.debug("Assuming IrisDimension: " + dim.getName()); + + IrisWorld w = IrisWorld.builder() + .name(worldName) + .seed(1337) + .environment(dim.getEnvironment()) + .worldFolder(new File(Bukkit.getWorldContainer(), worldName)) + .minHeight(dim.getMinHeight()) + .maxHeight(dim.getMaxHeight()) + .build(); + + Iris.debug("Generator Config: " + w.toString()); + + File ff = new File(w.worldFolder(), "iris/pack"); + if (!ff.exists() || ff.listFiles().length == 0) { + ff.mkdirs(); + service(StudioSVC.class).installIntoWorld(getSender(), dim.getLoadKey(), w.worldFolder()); + } + + return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey()); + } + + public void splash() { + if (!IrisSettings.get().getGeneral().isSplashLogoStartup()) { + return; + } + + String padd = Form.repeat(" ", 8); + String padd2 = Form.repeat(" ", 4); + String[] info = {"", "", "", "", "", padd2 + C.IRIS + " Iris", padd2 + C.GRAY + " by " + "Volmit Software", padd2 + C.GRAY + " v" + C.IRIS + getDescription().getVersion()}; + if (unstablemode) { + info = new String[]{"", "", "", "", "", padd2 + C.RED + " Iris", padd2 + C.GRAY + " by " + C.DARK_RED + "Volmit Software", padd2 + C.GRAY + " v" + C.RED + getDescription().getVersion()}; + } + if (warningmode) { + info = new String[]{"", "", "", "", "", padd2 + C.GOLD + " Iris", padd2 + C.GRAY + " by " + C.GOLD + "Volmit Software", padd2 + C.GRAY + " v" + C.GOLD + getDescription().getVersion()}; + } + + String[] splashstable = { + padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@", + padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + C.IRIS + " .(((()))). ", + padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + C.IRIS + " .((((((())))))). ", + padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + C.IRIS + " ((((((((())))))))) " + C.GRAY + " @", + padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + C.IRIS + " ((((((((-))))))))) " + C.GRAY + " @@", + padd + C.GRAY + "@@@&&" + C.IRIS + " ((((((({ })))))))) " + C.GRAY + " &&@@@", + padd + C.GRAY + "@@" + C.IRIS + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@", + padd + C.GRAY + "@" + C.IRIS + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@", + padd + C.GRAY + "" + C.IRIS + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@", + padd + C.GRAY + "" + C.IRIS + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@", + padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@" + }; + + String[] splashunstable = { + padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@", + padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + C.RED + " .(((()))). ", + padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + C.RED + " .((((((())))))). ", + padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + C.RED + " ((((((((())))))))) " + C.GRAY + " @", + padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + C.RED + " ((((((((-))))))))) " + C.GRAY + " @@", + padd + C.GRAY + "@@@&&" + C.RED + " ((((((({ })))))))) " + C.GRAY + " &&@@@", + padd + C.GRAY + "@@" + C.RED + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@", + padd + C.GRAY + "@" + C.RED + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@", + padd + C.GRAY + "" + C.RED + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@", + padd + C.GRAY + "" + C.RED + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@", + padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@" + }; + String[] splashwarning = { + padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@", + padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + C.GOLD + " .(((()))). ", + padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + C.GOLD + " .((((((())))))). ", + padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + C.GOLD + " ((((((((())))))))) " + C.GRAY + " @", + padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + C.GOLD + " ((((((((-))))))))) " + C.GRAY + " @@", + padd + C.GRAY + "@@@&&" + C.GOLD + " ((((((({ })))))))) " + C.GRAY + " &&@@@", + padd + C.GRAY + "@@" + C.GOLD + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@", + padd + C.GRAY + "@" + C.GOLD + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@", + padd + C.GRAY + "" + C.GOLD + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@", + padd + C.GRAY + "" + C.GOLD + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@", + padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@" + }; + String[] splash; + File freeSpace = new File(Bukkit.getWorldContainer() + "."); + if (unstablemode) { + splash = splashunstable; + } else if (warningmode) { + splash = splashwarning; + } else { + splash = splashstable; + } + + if (!passedserversoftware) { + Iris.info("Server type & version: " + C.RED + Bukkit.getVersion()); + } else { Iris.info("Server type & version: " + Bukkit.getVersion()); } + Iris.info("Java: " + getJava()); + if (getHardware.getProcessMemory() < 5999) { + Iris.warn("6GB+ Ram is recommended"); + Iris.warn("Process Memory: " + getHardware.getProcessMemory() + " MB"); + } + Iris.info("Bukkit distro: " + Bukkit.getName()); + Iris.info("Custom Biomes: " + INMS.get().countCustomBiomes()); + setupChecks(); + printPacks(); + + for (int i = 0; i < info.length; i++) { + splash[i] += info[i]; + } + + Iris.info("\n\n " + new KList<>(splash).toString("\n") + "\n"); + } + + private void printPacks() { + File packFolder = Iris.service(StudioSVC.class).getWorkspaceFolder(); + File[] packs = packFolder.listFiles(File::isDirectory); + if (packs == null || packs.length == 0) + return; + Iris.info("Custom Dimensions: " + packs.length); + for (File f : packs) + printPack(f); + } + + private void printPack(File pack) { + String dimName = pack.getName(); + String version = "???"; + try (FileReader r = new FileReader(new File(pack, "dimensions/" + dimName + ".json"))) { + JsonObject json = JsonParser.parseReader(r).getAsJsonObject(); + if (json.has("version")) + version = json.get("version").getAsString(); + } catch (IOException | JsonParseException ignored) { + } + Iris.info(" " + dimName + " v" + version); + } + + public int getIrisVersion() { + String input = Iris.instance.getDescription().getVersion(); + int hyphenIndex = input.indexOf('-'); + if (hyphenIndex != -1) { + String result = input.substring(0, hyphenIndex); + result = result.replaceAll("\\.", ""); + return Integer.parseInt(result); + } + return -1; + } + + public int getMCVersion() { + try { + String version = Bukkit.getVersion(); + Matcher matcher = Pattern.compile("\\(MC: ([\\d.]+)\\)").matcher(version); + if (matcher.find()) { + version = matcher.group(1).replaceAll("\\.", ""); + long versionNumber = Long.parseLong(version); + if (versionNumber > Integer.MAX_VALUE) { + return -1; + } + return (int) versionNumber; + } + return -1; + } catch (Exception e) { + return -1; + } + } + + private static boolean suppress(Throwable e) { + return (e instanceof IllegalStateException ex && "zip file closed".equals(ex.getMessage())) || e instanceof JSONException; + } + + private static void setupSentry() { + var settings = IrisSettings.get().getSentry(); + if (settings.disableAutoReporting || Sentry.isEnabled()) return; + Iris.info("Enabling Sentry for anonymous error reporting. You can disable this in the settings."); + Iris.info("Your server ID is: " + ServerID.ID); + Sentry.init(options -> { + options.setDsn("https://b16ecc222e9c1e0c48faecacb906fd89@o4509451052646400.ingest.de.sentry.io/4509452722765904"); + if (settings.debug) { + options.setLogger(new IrisLogger()); + options.setDebug(true); + } + + options.setAttachServerName(false); + options.setEnableUncaughtExceptionHandler(false); + options.setRelease(Iris.instance.getDescription().getVersion()); + options.setBeforeSend((event, hint) -> { + if (suppress(event.getThrowable())) return null; + event.setTag("iris.safeguard", IrisSafeguard.mode()); + event.setTag("iris.nms", INMS.get().getClass().getCanonicalName()); + var context = IrisContext.get(); + if (context != null) event.getContexts().set("engine", context.asContext()); + return event; + }); + }); + Sentry.configureScope(scope -> { + if (settings.includeServerId) scope.setUser(ServerID.asUser()); + scope.addAttachment(Attachments.PLUGINS); + scope.setTag("server", Bukkit.getVersion()); + scope.setTag("server.type", Bukkit.getName()); + scope.setTag("server.api", Bukkit.getBukkitVersion()); + }); + Runtime.getRuntime().addShutdownHook(new Thread(Sentry::close)); + } +} diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 7804b3eac..c409c1f9d 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -225,6 +225,7 @@ public class IrisSettings { @Data public static class IrisSettingsSentry { + public boolean includeServerId = true; public boolean disableAutoReporting = false; public boolean debug = false; } diff --git a/core/src/main/java/com/volmit/iris/util/sentry/ServerID.java b/core/src/main/java/com/volmit/iris/util/sentry/ServerID.java new file mode 100644 index 000000000..da766574e --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/sentry/ServerID.java @@ -0,0 +1,59 @@ +package com.volmit.iris.util.sentry; + +import com.volmit.iris.util.io.IO; +import io.sentry.protocol.User; +import lombok.SneakyThrows; +import org.bukkit.Bukkit; +import oshi.SystemInfo; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class ServerID { + public static final String ID = calculate(); + + public static User asUser() { + User u = new User(); + u.setId(ID); + return u; + } + + @SneakyThrows + private static String calculate() { + Digest md = new Digest(); + md.update(System.getProperty("java.vm.name")); + md.update(System.getProperty("java.vm.version")); + md.update(new SystemInfo().getHardware().getProcessor().toString()); + md.update(Runtime.getRuntime().maxMemory()); + for (var p : Bukkit.getPluginManager().getPlugins()) { + md.update(p.getName()); + } + + return IO.bytesToHex(md.digest()); + } + + private static final class Digest { + private final MessageDigest md = MessageDigest.getInstance("SHA-256"); + private final byte[] buffer = new byte[8]; + private final ByteBuffer wrapped = ByteBuffer.wrap(buffer); + + private Digest() throws NoSuchAlgorithmException { + } + + public void update(String string) { + if (string == null) return; + md.update(string.getBytes(StandardCharsets.UTF_8)); + } + + public void update(long Long) { + wrapped.putLong(0, Long); + md.update(buffer, 0, 8); + } + + public byte[] digest() { + return md.digest(); + } + } +} From f32f73e65af9717561865d8ac7f3ca8adf87a391 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 13 Jun 2025 12:27:03 +0200 Subject: [PATCH 082/138] disable error reporting in dev environment --- build.gradle.kts | 2 ++ core/src/main/java/com/volmit/iris/Iris.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index bb061f2bc..86a2170c1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -60,6 +60,7 @@ val serverMinHeap = "2G" val serverMaxHeap = "8G" //Valid values are: none, truecolor, indexed256, indexed16, indexed8 val color = "truecolor" +val errorReporting = false val nmsBindings = mapOf( "v1_21_R4" to "1.21.5-R0.1-SNAPSHOT", @@ -103,6 +104,7 @@ nmsBindings.forEach { key, value -> systemProperty("disable.watchdog", "") systemProperty("net.kyori.ansi.colorLevel", color) systemProperty("com.mojang.eula.agree", true) + systemProperty("iris.errorReporting", errorReporting) } } diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 08d5c9736..a33f4cbfa 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -955,7 +955,7 @@ public class Iris extends VolmitPlugin implements Listener { private static void setupSentry() { var settings = IrisSettings.get().getSentry(); - if (settings.disableAutoReporting || Sentry.isEnabled()) return; + if (settings.disableAutoReporting || Sentry.isEnabled() || !Boolean.getBoolean("iris.errorReporting")) return; Iris.info("Enabling Sentry for anonymous error reporting. You can disable this in the settings."); Iris.info("Your server ID is: " + ServerID.ID); Sentry.init(options -> { From 01b62c13b60d793b0cb2fecd32117701e1f50dd3 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 14 Jun 2025 11:16:47 +0200 Subject: [PATCH 083/138] fix deleting mantle temp files before they are fully written --- core/src/main/java/com/volmit/iris/util/mantle/Mantle.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java index 6ec24717d..6e1e56e54 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -369,11 +369,10 @@ public class Mantle { */ public synchronized void close() { Iris.debug("Closing The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath()); - if (closed.get()) { + if (closed.getAndSet(true)) { return; } - closed.set(true); hyperLock.disable(); BurstExecutor b = ioBurst.burst(toUnload.size()); loadedRegions.forEach((i, plate) -> b.queue(() -> { @@ -383,11 +382,11 @@ public class Mantle { oldFileForRegion(dataFolder, i).delete(); } catch (Throwable e) { Iris.error("Failed to write Tectonic Plate " + C.DARK_GREEN + Cache.keyX(i) + " " + Cache.keyZ(i)); + Iris.reportError(e); e.printStackTrace(); } })); loadedRegions.clear(); - IO.delete(new File(dataFolder, ".tmp")); try { b.complete(); @@ -395,6 +394,7 @@ public class Mantle { Iris.reportError(e); } + IO.delete(new File(dataFolder, ".tmp")); Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath()); } From 8df6253604e905dbbe91b425497d0c664da79939 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 14 Jun 2025 11:44:30 +0200 Subject: [PATCH 084/138] add safeguard info to sentry reports --- core/src/main/java/com/volmit/iris/Iris.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index a33f4cbfa..65b78aa43 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -30,6 +30,7 @@ import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.v1X.NMSBinding1X; import com.volmit.iris.core.pregenerator.LazyPregenerator; +import com.volmit.iris.core.safeguard.ServerBootSFG; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.EnginePanic; @@ -974,6 +975,7 @@ public class Iris extends VolmitPlugin implements Listener { event.setTag("iris.nms", INMS.get().getClass().getCanonicalName()); var context = IrisContext.get(); if (context != null) event.getContexts().set("engine", context.asContext()); + event.getContexts().set("safeguard", ServerBootSFG.allIncompatibilities); return event; }); }); From 32d9a5e40ad775118418effb726a4e04438f915d Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 14 Jun 2025 11:46:51 +0200 Subject: [PATCH 085/138] make sentry engine context hotload safe --- .../main/java/com/volmit/iris/util/context/IrisContext.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/util/context/IrisContext.java b/core/src/main/java/com/volmit/iris/util/context/IrisContext.java index b076b5eef..73bae0e5e 100644 --- a/core/src/main/java/com/volmit/iris/util/context/IrisContext.java +++ b/core/src/main/java/com/volmit/iris/util/context/IrisContext.java @@ -96,8 +96,8 @@ public class IrisContext { .qput("studio", engine.isStudio()) .qput("closed", engine.isClosed()) .qput("pack", new KMap<>() - .qput("key", dimension.getLoadKey()) - .qput("version", dimension.getVersion()) + .qput("key", dimension == null ? "" : dimension.getLoadKey()) + .qput("version", dimension == null ? "" : dimension.getVersion()) .qput("hash", hash32 == null ? "" : Long.toHexString(hash32))) .qput("mantle", new KMap<>() .qput("idle", mantle.getAdjustedIdleDuration()) From 840608a40f8c7219da24c4d78fd103c7bf72acab Mon Sep 17 00:00:00 2001 From: Aidan Aeternum Date: Sat, 14 Jun 2025 16:49:22 -0400 Subject: [PATCH 086/138] v+ --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 86a2170c1..3094d704e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,7 +36,7 @@ plugins { id("io.sentry.jvm.gradle") version "5.7.0" } -version = "3.6.10-1.20.1-1.21.5" +version = "3.6.11-1.20.1-1.21.5" // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED // ======================== WINDOWS ============================= @@ -275,4 +275,4 @@ fun registerCustomOutputTaskUnix(name: String, path: String) { into(file(path)) rename { "Iris.jar" } } -} \ No newline at end of file +} From da2dd42e289e28de8cc0984ee335003baf2d5edb Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 16 Jun 2025 16:59:24 +0200 Subject: [PATCH 087/138] invert suppressed error reporting for dev enviroments --- build.gradle.kts | 2 +- core/src/main/java/com/volmit/iris/Iris.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3094d704e..8fc40d64c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -104,7 +104,7 @@ nmsBindings.forEach { key, value -> systemProperty("disable.watchdog", "") systemProperty("net.kyori.ansi.colorLevel", color) systemProperty("com.mojang.eula.agree", true) - systemProperty("iris.errorReporting", errorReporting) + systemProperty("iris.suppressReporting", !errorReporting) } } diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 65b78aa43..4fde24ec9 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -956,7 +956,7 @@ public class Iris extends VolmitPlugin implements Listener { private static void setupSentry() { var settings = IrisSettings.get().getSentry(); - if (settings.disableAutoReporting || Sentry.isEnabled() || !Boolean.getBoolean("iris.errorReporting")) return; + if (settings.disableAutoReporting || Sentry.isEnabled() || Boolean.getBoolean("iris.suppressReporting")) return; Iris.info("Enabling Sentry for anonymous error reporting. You can disable this in the settings."); Iris.info("Your server ID is: " + ServerID.ID); Sentry.init(options -> { From d048c073ac2de1c29285b3e322cbd892fea05a23 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 16 Jun 2025 22:53:11 +0200 Subject: [PATCH 088/138] disable trim for fallback pack download --- core/src/main/java/com/volmit/iris/Iris.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 4fde24ec9..37ce7e686 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -782,7 +782,7 @@ public class Iris extends VolmitPlugin implements Listener { if (dim == null) { Iris.warn("Unable to find dimension type " + id + " Looking for online packs..."); - service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, true); + service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, false); dim = IrisData.loadAnyDimension(id); if (dim == null) { From c9c8a9e4128e486f4efb3413c45f1ce1d8271fb7 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 16 Jun 2025 22:53:56 +0200 Subject: [PATCH 089/138] fix null pointers in pack trim method --- .../main/java/com/volmit/iris/core/project/IrisProject.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/project/IrisProject.java b/core/src/main/java/com/volmit/iris/core/project/IrisProject.java index d094c82ff..c0f327b6f 100644 --- a/core/src/main/java/com/volmit/iris/core/project/IrisProject.java +++ b/core/src/main/java/com/volmit/iris/core/project/IrisProject.java @@ -377,17 +377,17 @@ public class IrisProject { KSet loot = new KSet<>(); KSet blocks = new KSet<>(); - for (String i : dm.getDimensionLoader().getPossibleKeys()) { + for (String i : dm.getBlockLoader().getPossibleKeys()) { blocks.add(dm.getBlockLoader().load(i)); } dimension.getRegions().forEach((i) -> regions.add(dm.getRegionLoader().load(i))); dimension.getLoot().getTables().forEach((i) -> loot.add(dm.getLootLoader().load(i))); - regions.forEach((i) -> biomes.addAll(i.getAllBiomes(null))); + regions.forEach((i) -> biomes.addAll(i.getAllBiomes(() -> dm))); regions.forEach((r) -> r.getLoot().getTables().forEach((i) -> loot.add(dm.getLootLoader().load(i)))); regions.forEach((r) -> r.getEntitySpawners().forEach((sp) -> spawners.add(dm.getSpawnerLoader().load(sp)))); dimension.getEntitySpawners().forEach((sp) -> spawners.add(dm.getSpawnerLoader().load(sp))); - biomes.forEach((i) -> i.getGenerators().forEach((j) -> generators.add(j.getCachedGenerator(null)))); + biomes.forEach((i) -> i.getGenerators().forEach((j) -> generators.add(j.getCachedGenerator(() -> dm)))); biomes.forEach((r) -> r.getLoot().getTables().forEach((i) -> loot.add(dm.getLootLoader().load(i)))); biomes.forEach((r) -> r.getEntitySpawners().forEach((sp) -> spawners.add(dm.getSpawnerLoader().load(sp)))); spawners.forEach((i) -> i.getSpawns().forEach((j) -> entities.add(dm.getEntityLoader().load(j.getEntity())))); From 80548f753c71afea4a9bf531cb9bbbda36cfd7f1 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 21 Jun 2025 11:48:58 +0200 Subject: [PATCH 090/138] isolate java agent to prevent class loader issues --- build.gradle.kts | 14 ++------------ core/agent/build.gradle.kts | 12 ++++++++++++ .../java/com/volmit/iris/util/agent/Installer.java | 0 .../com/volmit/iris/core/safeguard/UtilsSFG.java | 3 ++- .../java/com/volmit/iris/util/agent/Agent.java | 7 ++++++- settings.gradle.kts | 2 +- 6 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 core/agent/build.gradle.kts rename core/{ => agent}/src/main/java/com/volmit/iris/util/agent/Installer.java (100%) diff --git a/build.gradle.kts b/build.gradle.kts index 6c13f9c12..89b226ee0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -106,7 +106,7 @@ nmsBindings.forEach { key, value -> systemProperty("net.kyori.ansi.colorLevel", color) systemProperty("com.mojang.eula.agree", true) systemProperty("iris.errorReporting", errorReporting) - jvmArgs("-javaagent:${tasks.jar.flatMap { it.archiveFile }.get().asFile.absolutePath}") + jvmArgs("-javaagent:${project(":core:agent").tasks.jar.flatMap { it.archiveFile }.get().asFile.absolutePath}") } } @@ -117,14 +117,8 @@ tasks { from(project(":nms:$key").tasks.named("remap").map { zipTree(it.outputs.files.singleFile) }) } from(project(":core").tasks.shadowJar.flatMap { it.archiveFile }.map { zipTree(it) }) + from(project(":core:agent").tasks.jar.flatMap { it.archiveFile }) archiveFileName.set("Iris-${project.version}.jar") - - manifest.attributes( - "Agent-Class" to "com.volmit.iris.util.agent.Installer", - "Premain-Class" to "com.volmit.iris.util.agent.Installer", - "Can-Redefine-Classes" to true, - "Can-Retransform-Classes" to true - ) } register("iris") { @@ -177,10 +171,6 @@ fun exec(vararg command: Any) { p.waitFor() } -dependencies { - implementation(project(":core")) -} - configurations.configureEach { resolutionStrategy.cacheChangingModulesFor(60, "minutes") resolutionStrategy.cacheDynamicVersionsFor(60, "minutes") diff --git a/core/agent/build.gradle.kts b/core/agent/build.gradle.kts new file mode 100644 index 000000000..a0d8024df --- /dev/null +++ b/core/agent/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + java +} + +tasks.jar { + manifest.attributes( + "Agent-Class" to "com.volmit.iris.util.agent.Installer", + "Premain-Class" to "com.volmit.iris.util.agent.Installer", + "Can-Redefine-Classes" to true, + "Can-Retransform-Classes" to true + ) +} \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/util/agent/Installer.java b/core/agent/src/main/java/com/volmit/iris/util/agent/Installer.java similarity index 100% rename from core/src/main/java/com/volmit/iris/util/agent/Installer.java rename to core/agent/src/main/java/com/volmit/iris/util/agent/Installer.java diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java index e3deb6f2b..b59a24585 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java @@ -1,6 +1,7 @@ package com.volmit.iris.core.safeguard; import com.volmit.iris.Iris; +import com.volmit.iris.util.agent.Agent; import com.volmit.iris.util.format.C; public class UtilsSFG { @@ -47,7 +48,7 @@ public class UtilsSFG { if (ServerBootSFG.missingAgent) { Iris.safeguard(C.RED + "Java Agent"); Iris.safeguard(C.RED + "- Please enable dynamic agent loading by adding -XX:+EnableDynamicAgentLoading to your jvm arguments."); - Iris.safeguard(C.RED + "- or add the jvm argument -javaagent:plugins/" + Iris.instance.getJarFile().getName()); + Iris.safeguard(C.RED + "- or add the jvm argument -javaagent:" + Agent.AGENT_JAR.getPath()); } if (!ServerBootSFG.passedserversoftware) { Iris.safeguard(C.YELLOW + "Unsupported Server Software"); diff --git a/core/src/main/java/com/volmit/iris/util/agent/Agent.java b/core/src/main/java/com/volmit/iris/util/agent/Agent.java index 009923b30..5f823fc30 100644 --- a/core/src/main/java/com/volmit/iris/util/agent/Agent.java +++ b/core/src/main/java/com/volmit/iris/util/agent/Agent.java @@ -4,10 +4,14 @@ import com.volmit.iris.Iris; import net.bytebuddy.agent.ByteBuddyAgent; import net.bytebuddy.dynamic.loading.ClassReloadingStrategy; +import java.io.File; import java.lang.instrument.Instrumentation; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; public class Agent { private static final String NAME = "com.volmit.iris.util.agent.Installer"; + public static final File AGENT_JAR = new File(Iris.instance.getDataFolder(), "agent.jar"); public static ClassReloadingStrategy installed() { return ClassReloadingStrategy.of(getInstrumentation()); @@ -23,8 +27,9 @@ public class Agent { if (doGetInstrumentation() != null) return true; try { + Files.copy(Iris.instance.getResource("agent.jar"), AGENT_JAR.toPath(), StandardCopyOption.REPLACE_EXISTING); Iris.info("Installing Java Agent..."); - ByteBuddyAgent.attach(Iris.instance.getJarFile(), ByteBuddyAgent.ProcessProvider.ForCurrentVm.INSTANCE); + ByteBuddyAgent.attach(AGENT_JAR, ByteBuddyAgent.ProcessProvider.ForCurrentVm.INSTANCE); } catch (Throwable e) { e.printStackTrace(); } diff --git a/settings.gradle.kts b/settings.gradle.kts index c45462895..862732b5a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -28,7 +28,7 @@ plugins { rootProject.name = "Iris" -include(":core") +include(":core", ":core:agent") include( ":nms:v1_21_R4", ":nms:v1_21_R3", From ad720f4aa22d5a1de2da685438041fdbd946bdfc Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 21 Jun 2025 11:51:49 +0200 Subject: [PATCH 091/138] fix worm using xStyle for y and z --- .../src/main/java/com/volmit/iris/engine/object/IrisWorm.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisWorm.java b/core/src/main/java/com/volmit/iris/engine/object/IrisWorm.java index d2f7e499c..e2c414533 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisWorm.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisWorm.java @@ -72,8 +72,8 @@ public class IrisWorm { KList pos = new KList<>(); KSet check = allowLoops ? null : new KSet<>(); CNG gx = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data); - CNG gy = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data); - CNG gz = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data); + CNG gy = yStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data); + CNG gz = zStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data); while (itr-- > 0) { IrisPosition current = new IrisPosition(Math.round(cx), Math.round(cy), Math.round(cz)); From 7f6d65a13e6c43b084a0665fdfcc00020d4326a3 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 4 Jun 2025 23:03:54 +0200 Subject: [PATCH 092/138] fix worm ignoring the breakSurface option --- .../main/java/com/volmit/iris/engine/object/IrisCave.java | 8 +++----- .../com/volmit/iris/engine/object/IrisCavePlacer.java | 2 +- .../java/com/volmit/iris/engine/object/IrisRavine.java | 3 +-- .../main/java/com/volmit/iris/engine/object/IrisWorm.java | 7 +++++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java index e0e818403..22a45ccc4 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java @@ -66,14 +66,12 @@ public class IrisCave extends IrisRegistrant { } public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) { - generate(writer, rng, engine, x, y, z, 0, -1); + generate(writer, rng, engine, x, y, z, 0, -1, true); } - public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) { - + public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint, boolean breakSurface) { double girth = getWorm().getGirth().get(rng, x, z, engine.getData()); - KList points = getWorm().generate(rng, engine.getData(), writer, verticalRange, x, y, z, (at) -> { - }); + KList points = getWorm().generate(rng, engine.getData(), writer, verticalRange, x, y, z, breakSurface, girth + 9); int highestWater = Math.max(waterHint, -1); if (highestWater == -1) { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java index 1a95493a1..eb97b14fd 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java @@ -96,7 +96,7 @@ public class IrisCavePlacer implements IRare { } try { - cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), recursion, waterHint); + cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), recursion, waterHint, breakSurface); } catch (Throwable e) { e.printStackTrace(); fail.set(true); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisRavine.java b/core/src/main/java/com/volmit/iris/engine/object/IrisRavine.java index e2d9a79e4..3edd37c29 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisRavine.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisRavine.java @@ -97,8 +97,7 @@ public class IrisRavine extends IrisRegistrant { } public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) { - KList pos = getWorm().generate(rng, engine.getData(), writer, null, x, y, z, (at) -> { - }); + KList pos = getWorm().generate(rng, engine.getData(), writer, null, x, y, z, true, 0); CNG dg = depthStyle.getGenerator().createNoCache(rng, engine.getData()); CNG bw = baseWidthStyle.getGenerator().createNoCache(rng, engine.getData()); int highestWater = Math.max(waterHint, -1); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisWorm.java b/core/src/main/java/com/volmit/iris/engine/object/IrisWorm.java index e2c414533..b50f9d3be 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisWorm.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisWorm.java @@ -62,7 +62,7 @@ public class IrisWorm { private IrisStyledRange girth = new IrisStyledRange().setMin(3).setMax(5) .setStyle(new IrisGeneratorStyle(NoiseStyle.PERLIN)); - public KList generate(RNG rng, IrisData data, MantleWriter writer, IrisRange verticalRange, int x, int y, int z, Consumer fork) { + public KList generate(RNG rng, IrisData data, MantleWriter writer, IrisRange verticalRange, int x, int y, int z, boolean breakSurface, double distance) { int itr = maxIterations; double jx, jy, jz; double cx = x; @@ -77,7 +77,6 @@ public class IrisWorm { while (itr-- > 0) { IrisPosition current = new IrisPosition(Math.round(cx), Math.round(cy), Math.round(cz)); - fork.accept(current); pos.add(current); if (check != null) { @@ -92,6 +91,10 @@ public class IrisWorm { cz += jz; IrisPosition next = new IrisPosition(Math.round(cx), Math.round(cy), Math.round(cz)); + if (!breakSurface && writer.getEngineMantle().getHighest(next.getX(), next.getZ(), true) <= next.getY() + distance) { + break; + } + if (verticalRange != null && !verticalRange.contains(next.getY())) { break; } From 9d44ac0b47e7bbb8dfffef0c61a67051f7b1aab7 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 21 Jun 2025 12:04:07 +0200 Subject: [PATCH 093/138] fix caves being non-deterministic --- .../com/volmit/iris/engine/object/IrisCavePlacer.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java index eb97b14fd..c56a53d69 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java @@ -86,13 +86,9 @@ public class IrisCavePlacer implements IRare { } if (y == -1) { - if(!breakSurface) { - int eH = engine.getHeight(x, z); - if (caveStartHeight.getMax() > eH) { - caveStartHeight.setMax(eH); - } - } - y = (int) caveStartHeight.get(rng, x, z, data); + int h = (int) caveStartHeight.get(rng, x, z, data); + int ma = breakSurface ? h : (int) (engine.getComplex().getHeightStream().get(x, z) - 9); + y = Math.min(h, ma); } try { From e5cb4d82a34796b3666d95c321e956abf977e6af Mon Sep 17 00:00:00 2001 From: Julian Krings <47589149+CrazyDev05@users.noreply.github.com> Date: Sat, 21 Jun 2025 12:05:56 +0200 Subject: [PATCH 094/138] Add KGenerators support (#1209) --- core/build.gradle.kts | 3 + .../core/link/KGeneratorsDataProvider.java | 88 +++++++++++++++++++ .../iris/core/service/ExternalDataSVC.java | 4 + 3 files changed, 95 insertions(+) create mode 100644 core/src/main/java/com/volmit/iris/core/link/KGeneratorsDataProvider.java diff --git a/core/build.gradle.kts b/core/build.gradle.kts index cebfbc6c5..50ff813f0 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -60,6 +60,9 @@ dependencies { compileOnly("com.github.Ssomar-Developement:SCore:4.23.10.8") compileOnly("net.Indyuce:MMOItems-API:6.9.5-SNAPSHOT") compileOnly("com.willfp:EcoItems:5.44.0") + compileOnly("me.kryniowesegryderiusz:kgenerators-core:7.3") { + isTransitive = false + } //implementation files("libs/CustomItems.jar") diff --git a/core/src/main/java/com/volmit/iris/core/link/KGeneratorsDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/KGeneratorsDataProvider.java new file mode 100644 index 000000000..618fe4ccc --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/link/KGeneratorsDataProvider.java @@ -0,0 +1,88 @@ +package com.volmit.iris.core.link; + +import com.volmit.iris.core.service.ExternalDataSVC; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.data.B; +import com.volmit.iris.util.data.IrisCustomData; +import me.kryniowesegryderiusz.kgenerators.Main; +import me.kryniowesegryderiusz.kgenerators.api.KGeneratorsAPI; +import me.kryniowesegryderiusz.kgenerators.generators.locations.objects.GeneratorLocation; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.MissingResourceException; + +public class KGeneratorsDataProvider extends ExternalDataProvider { + public KGeneratorsDataProvider() { + super("KGenerators"); + } + + @Override + public void init() { + + } + + @Override + public @NotNull BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap state) throws MissingResourceException { + if (Main.getGenerators().get(blockId.key()) == null) throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); + return new IrisCustomData(Material.STRUCTURE_VOID.createBlockData(), ExternalDataSVC.buildState(blockId, state)); + } + + @Override + public @NotNull ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap customNbt) throws MissingResourceException { + var gen = Main.getGenerators().get(itemId.key()); + if (gen == null) throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key()); + return gen.getGeneratorItem(); + } + + @Override + public void processUpdate(@NotNull Engine engine, @NotNull Block block, @NotNull Identifier blockId) { + if (block.getType() != Material.STRUCTURE_VOID) return; + var existing = KGeneratorsAPI.getLoadedGeneratorLocation(block.getLocation()); + if (existing != null) return; + block.setBlockData(B.getAir(), false); + var gen = Main.getGenerators().get(blockId.key()); + if (gen == null) return; + var loc = new GeneratorLocation(-1, gen, block.getLocation(), Main.getPlacedGenerators().getChunkInfo(block.getChunk()), null, null); + Main.getDatabases().getDb().saveGenerator(loc); + Main.getPlacedGenerators().addLoaded(loc); + Main.getSchedules().schedule(loc, true); + } + + @Override + public @NotNull Identifier[] getBlockTypes() { + return Main.getGenerators().getAll().stream() + .map(gen -> new Identifier("kgenerators", gen.getId())) + .filter(i -> { + try { + return getBlockData(i) != null; + } catch (MissingResourceException e) { + return false; + } + }) + .toArray(Identifier[]::new); + } + + @Override + public @NotNull Identifier[] getItemTypes() { + return Main.getGenerators().getAll().stream() + .map(gen -> new Identifier("kgenerators", gen.getId())) + .filter(i -> { + try { + return getItemStack(i) != null; + } catch (MissingResourceException e) { + return false; + } + }) + .toArray(Identifier[]::new); + } + + @Override + public boolean isValidProvider(@NotNull Identifier id, boolean isItem) { + return "kgenerators".equalsIgnoreCase(id.namespace()); + } +} diff --git a/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java b/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java index c71224a99..4d9596278 100644 --- a/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java @@ -75,6 +75,10 @@ public class ExternalDataSVC implements IrisService { if (Bukkit.getPluginManager().getPlugin("EcoItems") != null) { Iris.info("EcoItems found, loading EcoItemsDataProvider..."); } + providers.add(new KGeneratorsDataProvider()); + if (Bukkit.getPluginManager().getPlugin("KGenerators") != null) { + Iris.info("KGenerators found, loading KGeneratorsDataProvider..."); + } for (ExternalDataProvider p : providers) { if (p.isReady()) { From 0a0f77ff585f1c1b5cdda9fefeff3c0e1380a86b Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 23 Jun 2025 12:20:43 +0200 Subject: [PATCH 095/138] fix splash being shown twice --- core/src/main/java/com/volmit/iris/Iris.java | 5 ++--- .../volmit/iris/core/safeguard/IrisSafeguard.java | 13 +++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index e472df55a..b38379d26 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -470,7 +470,7 @@ public class Iris extends VolmitPlugin implements Listener { ServerConfigurator.configure(); IrisSafeguard.IrisSafeguardSystem(); getSender().setTag(getTag()); - IrisSafeguard.earlySplash(); + IrisSafeguard.splash(true); linkMultiverseCore = new MultiverseCoreLink(); linkMythicMobs = new MythicMobsLink(); configWatcher = new FileWatcher(getDataFile("settings.json")); @@ -485,8 +485,7 @@ public class Iris extends VolmitPlugin implements Listener { J.sr(this::tickQueue, 0); J.s(this::setupPapi); J.a(ServerConfigurator::configure, 20); - splash(); - UtilsSFG.splash(); + IrisSafeguard.splash(false); autoStartStudio(); checkForBukkitWorlds(); diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java b/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java index 2c9eb9be1..241bdd01f 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java @@ -3,7 +3,10 @@ package com.volmit.iris.core.safeguard; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; +import java.util.concurrent.atomic.AtomicBoolean; + public class IrisSafeguard { + private static final AtomicBoolean sfg = new AtomicBoolean(false); public static boolean unstablemode = false; public static boolean warningmode = false; public static boolean stablemode = false; @@ -13,12 +16,14 @@ public class IrisSafeguard { ServerBootSFG.BootCheck(); } - public static void earlySplash() { - if (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode) + public static void splash(boolean early) { + if (early && (ServerBootSFG.safeguardPassed || IrisSettings.get().getGeneral().DoomsdayAnnihilationSelfDestructMode)) return; - Iris.instance.splash(); - UtilsSFG.splash(); + if (!sfg.getAndSet(true)) { + Iris.instance.splash(); + UtilsSFG.splash(); + } } public static String mode() { From 67398174bb889874c08b16bea7527ce6e68cf40a Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 23 Jun 2025 12:25:07 +0200 Subject: [PATCH 096/138] cache iris worlds in a map for datapack generation --- .../java/com/volmit/iris/core/IrisWorlds.java | 79 +++++++++++++++++++ .../volmit/iris/core/ServerConfigurator.java | 4 +- .../engine/platform/BukkitChunkGenerator.java | 53 ++++++------- .../main/java/com/volmit/iris/util/io/IO.java | 2 +- 4 files changed, 108 insertions(+), 30 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/core/IrisWorlds.java diff --git a/core/src/main/java/com/volmit/iris/core/IrisWorlds.java b/core/src/main/java/com/volmit/iris/core/IrisWorlds.java new file mode 100644 index 000000000..9c8302102 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/IrisWorlds.java @@ -0,0 +1,79 @@ +package com.volmit.iris.core; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.io.IO; +import org.bukkit.Bukkit; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.lang.reflect.Type; +import java.util.Objects; +import java.util.stream.Stream; + +public class IrisWorlds { + private static final AtomicCache cache = new AtomicCache<>(); + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static final Type TYPE = TypeToken.getParameterized(KMap.class, String.class, String.class).getType(); + private final KMap worlds; + private volatile boolean dirty = false; + + private IrisWorlds(KMap worlds) { + this.worlds = worlds; + save(); + } + + public static IrisWorlds get() { + return cache.aquire(() -> { + File file = Iris.instance.getDataFile("worlds.json"); + if (!file.exists()) { + return new IrisWorlds(new KMap<>()); + } + + try { + String json = IO.readAll(file); + KMap worlds = GSON.fromJson(json, TYPE); + return new IrisWorlds(Objects.requireNonNullElseGet(worlds, KMap::new)); + } catch (Throwable e) { + Iris.error("Failed to load worlds.json!"); + e.printStackTrace(); + Iris.reportError(e); + } + + return new IrisWorlds(new KMap<>()); + }); + } + + public void put(String name, String type) { + String old = worlds.put(name, type); + if (!type.equals(old)) + dirty = true; + save(); + } + + public Stream getFolders() { + return worlds.keySet().stream().map(k -> new File(Bukkit.getWorldContainer(), k)); + } + + public void clean() { + dirty = worlds.entrySet().removeIf(entry -> !new File(Bukkit.getWorldContainer(), entry.getKey() + "/iris/pack/dimensions/" + entry.getValue() + ".json").exists()); + } + + public synchronized void save() { + clean(); + if (!dirty) return; + try { + IO.write(Iris.instance.getDataFile("worlds.json"), OutputStreamWriter::new, writer -> GSON.toJson(worlds, TYPE, writer)); + dirty = false; + } catch (IOException e) { + Iris.error("Failed to save worlds.json!"); + e.printStackTrace(); + Iris.reportError(e); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java index f039f3b6d..79261d63d 100644 --- a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java +++ b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java @@ -226,8 +226,8 @@ public class ServerConfigurator { } public static Stream allPacks() { - return Stream.concat(listFiles(new File("plugins/Iris/packs")), - listFiles(Bukkit.getWorldContainer()).map(w -> new File(w, "iris/pack"))) + return Stream.concat(listFiles(Iris.instance.getDataFolder("packs")), + IrisWorlds.get().getFolders().map(w -> new File(w, "iris/pack"))) .filter(File::isDirectory) .filter( base -> { var content = new File(base, "dimensions").listFiles(); diff --git a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index e1c598fed..2e6035534 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -19,9 +19,9 @@ package com.volmit.iris.engine.platform; import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisWorlds; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.nms.INMS; -import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.engine.IrisEngine; import com.volmit.iris.engine.data.cache.AtomicCache; @@ -62,7 +62,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; -import java.lang.reflect.Field; import java.util.List; import java.util.Random; import java.util.concurrent.CompletableFuture; @@ -115,35 +114,35 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun @EventHandler(priority = EventPriority.LOWEST) public void onWorldInit(WorldInitEvent event) { - try { - if (initialized || !world.name().equals(event.getWorld().getName())) - return; - AutoClosing.closeContext(); - world.setRawWorldSeed(event.getWorld().getSeed()); - Engine engine = getEngine(event.getWorld()); - if (engine == null) { - Iris.warn("Failed to get Engine!"); - J.s(() -> { - Engine engine1 = getEngine(event.getWorld()); - if (engine1 != null) { - try { - INMS.get().inject(event.getWorld().getSeed(), engine1, event.getWorld()); - Iris.info("Injected Iris Biome Source into " + event.getWorld().getName()); - initialized = true; - } catch (Throwable e) { - e.printStackTrace(); - } - } - }, 10); - } else { - INMS.get().inject(event.getWorld().getSeed(), engine, event.getWorld()); - Iris.info("Injected Iris Biome Source into " + event.getWorld().getName()); - spawnChunks.complete(INMS.get().getSpawnChunkCount(event.getWorld())); - initialized = true; + if (initialized || !world.name().equals(event.getWorld().getName())) + return; + world.setRawWorldSeed(event.getWorld().getSeed()); + if (initialize(event.getWorld())) return; + + Iris.warn("Failed to get Engine for " + event.getWorld().getName() + " re-trying..."); + J.s(() -> { + if (!initialize(event.getWorld())) { + Iris.error("Failed to get Engine for " + event.getWorld().getName() + "!"); } + }, 10); + } + + private boolean initialize(World world) { + Engine engine = getEngine(world); + if (engine == null) return false; + try { + INMS.get().inject(world.getSeed(), engine, world); + Iris.info("Injected Iris Biome Source into " + world.getName()); } catch (Throwable e) { + Iris.reportError(e); + Iris.error("Failed to inject biome source into " + world.getName()); e.printStackTrace(); } + spawnChunks.complete(INMS.get().getSpawnChunkCount(world)); + Iris.instance.unregisterListener(this); + initialized = true; + IrisWorlds.get().put(world.getName(), dimensionKey); + return true; } @Nullable diff --git a/core/src/main/java/com/volmit/iris/util/io/IO.java b/core/src/main/java/com/volmit/iris/util/io/IO.java index 281f08096..61deb3670 100644 --- a/core/src/main/java/com/volmit/iris/util/io/IO.java +++ b/core/src/main/java/com/volmit/iris/util/io/IO.java @@ -1643,7 +1643,7 @@ public class IO { return (ch2 == -1); } - public static void write(File file, IOFunction builder, IOConsumer action) throws IOException { + public static void write(File file, IOFunction builder, IOConsumer action) throws IOException { File dir = new File(file.getParentFile(), ".tmp"); dir.mkdirs(); dir.deleteOnExit(); From e38dae0a328ead0a157737b84289edd81fff8610 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 23 Jun 2025 15:51:29 +0200 Subject: [PATCH 097/138] fix required properties for json schemas --- .../java/com/volmit/iris/core/project/SchemaBuilder.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java index 398d1630b..61d626247 100644 --- a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java +++ b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java @@ -140,6 +140,8 @@ public class SchemaBuilder { JSONObject property = buildProperty(k, c); + if (property.getBoolean("!required")) + required.put(k.getName()); property.remove("!required"); properties.put(k.getName(), property); } @@ -512,8 +514,9 @@ public class SchemaBuilder { d.add(fancyType); d.add(getDescription(k.getType())); - if (k.getType().isAnnotationPresent(Snippet.class)) { - String sm = k.getType().getDeclaredAnnotation(Snippet.class).value(); + Snippet snippet = k.getType().getDeclaredAnnotation(Snippet.class); + if (snippet != null) { + String sm = snippet.value(); d.add(" "); d.add("You can instead specify \"snippet/" + sm + "/some-name.json\" to use a snippet file instead of specifying it here."); } From 36779311148c987fc83a34126f934e1f89807b93 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 23 Jun 2025 15:51:53 +0200 Subject: [PATCH 098/138] improve snippet finder --- .../com/volmit/iris/core/loader/IrisData.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java index a272be0d7..52144fef5 100644 --- a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java +++ b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java @@ -44,6 +44,8 @@ import lombok.Data; import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Objects; import java.util.function.Function; @@ -427,8 +429,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { File f = new File(getDataFolder(), r + ".json"); if (f.exists()) { - try { - JsonReader snippetReader = new JsonReader(new FileReader(f)); + try (JsonReader snippetReader = new JsonReader(new FileReader(f))){ return adapter.read(snippetReader); } catch (Throwable e) { Iris.error("Couldn't read snippet " + r + " in " + reader.getPath() + " (" + e.getMessage() + ")"); @@ -462,11 +463,20 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { KList l = new KList<>(); File snippetFolder = new File(getDataFolder(), "snippet/" + f); + if (!snippetFolder.exists()) return l; - if (snippetFolder.exists() && snippetFolder.isDirectory()) { - for (File i : snippetFolder.listFiles()) { - l.add("snippet/" + f + "/" + i.getName().split("\\Q.\\E")[0]); - } + String absPath = snippetFolder.getAbsolutePath(); + try (var stream = Files.walk(snippetFolder.toPath())) { + stream.filter(Files::isRegularFile) + .map(Path::toAbsolutePath) + .map(Path::toString) + .filter(s -> s.endsWith(".json")) + .map(s -> s.substring(absPath.length() + 1)) + .map(s -> s.replace("\\", "/")) + .map(s -> s.split("\\Q.\\E")[0]) + .forEach(s -> l.add("snippet/" + f + "/" + s)); + } catch (Throwable e) { + e.printStackTrace(); } return l; From a802edc375810030dd297cb9bdb6e4ee098d7061 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 23 Jun 2025 17:16:45 +0200 Subject: [PATCH 099/138] add some options for customizing caves --- .../iris/engine/mantle/MantleWriter.java | 70 ++++++++++++++++--- .../volmit/iris/engine/object/IrisCave.java | 11 ++- .../iris/engine/object/IrisCaveShape.java | 52 ++++++++++++++ .../volmit/iris/util/mantle/MantleChunk.java | 10 ++- 4 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/engine/object/IrisCaveShape.java diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java b/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java index b71051472..ee2c9bf2d 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java @@ -35,6 +35,7 @@ import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.mantle.MantleChunk; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.matter.Matter; +import com.volmit.iris.util.noise.CNG; import lombok.Data; import org.bukkit.block.data.BlockData; import org.bukkit.util.Vector; @@ -71,6 +72,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { private static Set getBallooned(Set vset, double radius) { Set returnset = new HashSet<>(); int ceilrad = (int) Math.ceil(radius); + double r2 = Math.pow(radius, 2); for (IrisPosition v : vset) { int tipx = v.getX(); @@ -80,7 +82,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { for (int loopx = tipx - ceilrad; loopx <= tipx + ceilrad; loopx++) { for (int loopy = tipy - ceilrad; loopy <= tipy + ceilrad; loopy++) { for (int loopz = tipz - ceilrad; loopz <= tipz + ceilrad; loopz++) { - if (hypot(loopx - tipx, loopy - tipy, loopz - tipz) <= radius) { + if (hypot(loopx - tipx, loopy - tipy, loopz - tipz) <= r2) { returnset.add(new IrisPosition(loopx, loopy, loopz)); } } @@ -113,7 +115,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { for (double d : pars) { sum += Math.pow(d, 2); } - return Math.sqrt(sum); + return sum; } private static double lengthSq(double x, double y, double z) { @@ -453,6 +455,62 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { * @param the type of data to apply to the mantle */ public void setLineConsumer(List vectors, double radius, boolean filled, Function3 data) { + Set vset = cleanup(vectors); + vset = getBallooned(vset, radius); + + if (!filled) { + vset = getHollowed(vset); + } + + setConsumer(vset, data); + } + + /** + * Set lines for points + * + * @param vectors the points + * @param radius the radius + * @param filled hollow or filled? + * @param data the data to set + * @param the type of data to apply to the mantle + */ + public void setNoiseMasked(List vectors, double radius, double threshold, CNG shape, Set masks, boolean filled, Function3 data) { + Set vset = cleanup(vectors); + vset = masks == null ? getBallooned(vset, radius) : getMasked(vset, masks, radius); + vset.removeIf(p -> shape.noise(p.getX(), p.getY(), p.getZ()) < threshold); + + if (!filled) { + vset = getHollowed(vset); + } + + setConsumer(vset, data); + } + + private static Set getMasked(Set vectors, Set masks, double radius) { + Set vset = new KSet<>(); + int ceil = (int) Math.ceil(radius); + double r2 = Math.pow(radius, 2); + + for (IrisPosition v : vectors) { + int tipX = v.getX(); + int tipY = v.getY(); + int tipZ = v.getZ(); + + for (int x = -ceil; x <= ceil; x++) { + for (int y = -ceil; y <= ceil; y++) { + for (int z = -ceil; z <= ceil; z++) { + if (hypot(x, y, z) > r2 || !masks.contains(new IrisPosition(x, y, z))) + continue; + vset.add(new IrisPosition(tipX + x, tipY + y, tipZ + z)); + } + } + } + } + + return vset; + } + + private static Set cleanup(List vectors) { Set vset = new KSet<>(); for (int i = 0; vectors.size() != 0 && i < vectors.size() - 1; i++) { @@ -504,13 +562,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { } } - vset = getBallooned(vset, radius); - - if (!filled) { - vset = getHollowed(vset); - } - - setConsumer(vset, data); + return vset; } /** diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java index 22a45ccc4..db953f1a0 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java @@ -25,9 +25,11 @@ import com.volmit.iris.engine.mantle.MantleWriter; import com.volmit.iris.engine.object.annotations.Desc; import com.volmit.iris.engine.object.annotations.RegistryListResource; import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.matter.MatterCavern; +import com.volmit.iris.util.noise.CNG; import com.volmit.iris.util.plugin.VolmitSender; import lombok.AllArgsConstructor; import lombok.Data; @@ -55,6 +57,9 @@ public class IrisCave extends IrisRegistrant { @Desc("Limit the worm from ever getting higher or lower than this range") private IrisRange verticalRange = new IrisRange(3, 255); + @Desc("Shape of the caves") + private IrisCaveShape shape = new IrisCaveShape(); + @Override public String getFolderName() { return "caves"; @@ -96,8 +101,10 @@ public class IrisCave extends IrisRegistrant { MatterCavern c = new MatterCavern(true, customBiome, (byte) 0); MatterCavern w = new MatterCavern(true, customBiome, (byte) 1); - writer.setLineConsumer(points, - girth, true, + CNG cng = shape.getNoise(rng, engine); + KSet mask = shape.getMasked(rng, engine); + writer.setNoiseMasked(points, + girth, cng.noise(x, y, z), cng, mask, true, (xf, yf, zf) -> yf <= h ? w : c); } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCaveShape.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCaveShape.java new file mode 100644 index 000000000..f5001befb --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCaveShape.java @@ -0,0 +1,52 @@ +package com.volmit.iris.engine.object; + +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.object.annotations.Desc; +import com.volmit.iris.engine.object.annotations.RegistryListResource; +import com.volmit.iris.engine.object.annotations.Snippet; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.noise.CNG; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Snippet("cave-shape") +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Desc("Cave Shape") +@Data +public class IrisCaveShape { + private transient final KMap> cache = new KMap<>(); + + @Desc("Noise used for the shape of the cave") + private IrisGeneratorStyle noise = new IrisGeneratorStyle(); + @RegistryListResource(IrisObject.class) + @Desc("Object used as mask for the shape of the cave") + private String object = null; + @Desc("Rotation to apply to objects before using them as mask") + private IrisObjectRotation objectRotation = new IrisObjectRotation(); + + public CNG getNoise(RNG rng, Engine engine) { + return noise.create(rng, engine.getData()); + } + + public KSet getMasked(RNG rng, Engine engine) { + if (object == null) return null; + return cache.computeIfAbsent(new IrisPosition( + rng.i(0, 360), + rng.i(0, 360), + rng.i(0, 360)), + pos -> { + var rotated = new KSet(); + engine.getData().getObjectLoader().load(object).getBlocks().forEach((vector, data) -> { + if (data.getMaterial().isAir()) return; + rotated.add(new IrisPosition(objectRotation.rotate(vector, pos.getX(), pos.getY(), pos.getZ()))); + }); + return rotated; + }); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java b/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java index ae4fd1701..1c50d8c98 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java @@ -27,6 +27,7 @@ import com.volmit.iris.util.matter.IrisMatter; import com.volmit.iris.util.matter.Matter; import com.volmit.iris.util.matter.MatterSlice; import lombok.Getter; +import lombok.SneakyThrows; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; @@ -111,9 +112,11 @@ public class MantleChunk { } } - public void close() throws InterruptedException { + @SneakyThrows + public void close() { closed.set(true); ref.acquire(Integer.MAX_VALUE); + ref.release(Integer.MAX_VALUE); } public boolean inUse() { @@ -123,6 +126,10 @@ public class MantleChunk { public MantleChunk use() { if (closed.get()) throw new IllegalStateException("Chunk is closed!"); ref.acquireUninterruptibly(); + if (closed.get()) { + ref.release(); + throw new IllegalStateException("Chunk is closed!"); + } return this; } @@ -213,6 +220,7 @@ public class MantleChunk { * @throws IOException shit happens */ public void write(DataOutputStream dos) throws IOException { + close(); dos.writeByte(x); dos.writeByte(z); dos.writeByte(sections.length()); From cca0bed482c9d4eff8710a050b311ff1e16da089 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 24 Jun 2025 16:53:26 +0200 Subject: [PATCH 100/138] fix cave max size calculation --- core/src/main/java/com/volmit/iris/engine/object/IrisCave.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java index db953f1a0..bcfd2a05c 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java @@ -114,6 +114,6 @@ public class IrisCave extends IrisRegistrant { } public int getMaxSize(IrisData data, int depth) { - return getWorm().getMaxDistance() + fork.getMaxRange(data, depth); + return (int) (Math.ceil(getWorm().getGirth().getMax() * 2) + getWorm().getMaxDistance() + fork.getMaxRange(data, depth)); } } From cf8243a0008db153b8f92dd270c058f2be969437 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 24 Jun 2025 19:37:37 +0200 Subject: [PATCH 101/138] make caves more deterministic --- .../com/volmit/iris/engine/object/IrisCarving.java | 14 +++++++------- .../com/volmit/iris/engine/object/IrisCave.java | 14 +++++++------- .../volmit/iris/engine/object/IrisCavePlacer.java | 8 ++++---- .../com/volmit/iris/engine/object/IrisRavine.java | 12 ++++++------ .../iris/engine/object/IrisRavinePlacer.java | 6 +++--- .../com/volmit/iris/engine/object/IrisWorm.java | 6 +++--- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCarving.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCarving.java index 1da8b1a31..8287db8c4 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCarving.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCarving.java @@ -62,31 +62,31 @@ public class IrisCarving { @BlockCoordinates public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int depth) { - doCarving(writer, rng, engine, x, y, z, depth, -1); + doCarving(writer, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, depth, -1); } @BlockCoordinates - public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) { + public void doCarving(MantleWriter writer, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint) { int nextRecursion = recursion + 1; if (caves.isNotEmpty()) { for (IrisCavePlacer i : caves) { if (recursion > i.getMaxRecursion()) continue; - i.generateCave(writer, rng, engine, x, y, z, nextRecursion, waterHint); + i.generateCave(writer, rng, base, engine, x, y, z, nextRecursion, waterHint); } } if (ravines.isNotEmpty()) { for (IrisRavinePlacer i : ravines) { if (recursion > i.getMaxRecursion()) continue; - i.generateRavine(writer, rng, engine, x, y, z, nextRecursion, waterHint); + i.generateRavine(writer, rng, base, engine, x, y, z, nextRecursion, waterHint); } } if (spheres.isNotEmpty()) { for (IrisSphere i : spheres) { if (rng.nextInt(i.getRarity()) == 0) { - i.generate(rng, engine, writer, x, y, z); + i.generate(base, engine, writer, x, y, z); } } } @@ -94,7 +94,7 @@ public class IrisCarving { if (elipsoids.isNotEmpty()) { for (IrisElipsoid i : elipsoids) { if (rng.nextInt(i.getRarity()) == 0) { - i.generate(rng, engine, writer, x, y, z); + i.generate(base, engine, writer, x, y, z); } } } @@ -102,7 +102,7 @@ public class IrisCarving { if (pyramids.isNotEmpty()) { for (IrisPyramid i : pyramids) { if (rng.nextInt(i.getRarity()) == 0) { - i.generate(rng, engine, writer, x, y, z); + i.generate(base, engine, writer, x, y, z); } } } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java index bcfd2a05c..b780d257d 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCave.java @@ -71,12 +71,12 @@ public class IrisCave extends IrisRegistrant { } public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) { - generate(writer, rng, engine, x, y, z, 0, -1, true); + generate(writer, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, 0, -1, true); } - public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint, boolean breakSurface) { - double girth = getWorm().getGirth().get(rng, x, z, engine.getData()); - KList points = getWorm().generate(rng, engine.getData(), writer, verticalRange, x, y, z, breakSurface, girth + 9); + public void generate(MantleWriter writer, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint, boolean breakSurface) { + double girth = getWorm().getGirth().get(base.nextParallelRNG(465156), x, z, engine.getData()); + KList points = getWorm().generate(base.nextParallelRNG(784684), engine.getData(), writer, verticalRange, x, y, z, breakSurface, girth + 9); int highestWater = Math.max(waterHint, -1); if (highestWater == -1) { @@ -92,16 +92,16 @@ public class IrisCave extends IrisRegistrant { } - int h = Math.min(Math.max(highestWater, waterHint), engine.getDimension().getFluidHeight()); + int h = Math.min(highestWater, engine.getDimension().getFluidHeight()); for (IrisPosition i : points) { - fork.doCarving(writer, rng, engine, i.getX(), i.getY(), i.getZ(), recursion, h); + fork.doCarving(writer, rng, base, engine, i.getX(), i.getY(), i.getZ(), recursion, h); } MatterCavern c = new MatterCavern(true, customBiome, (byte) 0); MatterCavern w = new MatterCavern(true, customBiome, (byte) 1); - CNG cng = shape.getNoise(rng, engine); + CNG cng = shape.getNoise(base.nextParallelRNG(8131545), engine); KSet mask = shape.getMasked(rng, engine); writer.setNoiseMasked(points, girth, cng.noise(x, y, z), cng, mask, true, diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java index c56a53d69..ccdcaafb9 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCavePlacer.java @@ -64,10 +64,10 @@ public class IrisCavePlacer implements IRare { } public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) { - generateCave(mantle, rng, engine, x, y, z, 0, -1); + generateCave(mantle, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, 0, -1); } - public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) { + public void generateCave(MantleWriter mantle, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint) { if (fail.get()) { return; } @@ -86,13 +86,13 @@ public class IrisCavePlacer implements IRare { } if (y == -1) { - int h = (int) caveStartHeight.get(rng, x, z, data); + int h = (int) caveStartHeight.get(base, x, z, data); int ma = breakSurface ? h : (int) (engine.getComplex().getHeightStream().get(x, z) - 9); y = Math.min(h, ma); } try { - cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), recursion, waterHint, breakSurface); + cave.generate(mantle, rng, base, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), recursion, waterHint, breakSurface); } catch (Throwable e) { e.printStackTrace(); fail.set(true); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisRavine.java b/core/src/main/java/com/volmit/iris/engine/object/IrisRavine.java index 3edd37c29..b7c453bd6 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisRavine.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisRavine.java @@ -93,13 +93,13 @@ public class IrisRavine extends IrisRegistrant { } public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) { - generate(writer, rng, engine, x, y, z, 0, -1); + generate(writer, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, 0, -1); } - public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) { - KList pos = getWorm().generate(rng, engine.getData(), writer, null, x, y, z, true, 0); - CNG dg = depthStyle.getGenerator().createNoCache(rng, engine.getData()); - CNG bw = baseWidthStyle.getGenerator().createNoCache(rng, engine.getData()); + public void generate(MantleWriter writer, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint) { + KList pos = getWorm().generate(base.nextParallelRNG(879615), engine.getData(), writer, null, x, y, z, true, 0); + CNG dg = depthStyle.getGenerator().create(base.nextParallelRNG(7894156), engine.getData()); + CNG bw = baseWidthStyle.getGenerator().create(base.nextParallelRNG(15315456), engine.getData()); int highestWater = Math.max(waterHint, -1); boolean water = false; @@ -134,7 +134,7 @@ public class IrisRavine extends IrisRegistrant { int width = (int) Math.round(bw.fitDouble(baseWidthStyle.getMin(), baseWidthStyle.getMax(), p.getX(), p.getZ())); int surface = (int) Math.round(rsurface - depth * 0.45); - fork.doCarving(writer, rng, engine, p.getX(), rng.i(surface - depth, surface), p.getZ(), recursion, Math.max(highestWater, waterHint)); + fork.doCarving(writer, rng, base, engine, p.getX(), rng.i(surface - depth, surface), p.getZ(), recursion, highestWater); for (int i = surface + depth; i >= surface; i--) { if (i % ribThickness == 0) { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisRavinePlacer.java b/core/src/main/java/com/volmit/iris/engine/object/IrisRavinePlacer.java index fa65c5c79..ed9c01841 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisRavinePlacer.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisRavinePlacer.java @@ -60,10 +60,10 @@ public class IrisRavinePlacer implements IRare { } public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) { - generateRavine(mantle, rng, engine, x, y, z, 0, -1); + generateRavine(mantle, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, 0, -1); } - public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) { + public void generateRavine(MantleWriter mantle, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint) { if (fail.get()) { return; } @@ -84,7 +84,7 @@ public class IrisRavinePlacer implements IRare { try { int xx = x + rng.nextInt(15); int zz = z + rng.nextInt(15); - ravine.generate(mantle, rng, engine, xx, y, zz, recursion, waterHint); + ravine.generate(mantle, rng, base, engine, xx, y, zz, recursion, waterHint); } catch (Throwable e) { e.printStackTrace(); fail.set(true); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisWorm.java b/core/src/main/java/com/volmit/iris/engine/object/IrisWorm.java index b50f9d3be..e83fead07 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisWorm.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisWorm.java @@ -71,9 +71,9 @@ public class IrisWorm { IrisPosition start = new IrisPosition(x, y, z); KList pos = new KList<>(); KSet check = allowLoops ? null : new KSet<>(); - CNG gx = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data); - CNG gy = yStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data); - CNG gz = zStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data); + CNG gx = xStyle.getGenerator().create(rng.nextParallelRNG(14567), data); + CNG gy = yStyle.getGenerator().create(rng.nextParallelRNG(64789), data); + CNG gz = zStyle.getGenerator().create(rng.nextParallelRNG(34790), data); while (itr-- > 0) { IrisPosition current = new IrisPosition(Math.round(cx), Math.round(cy), Math.round(cz)); From 5d28563b7c09388c15a330f81c6dea38daadb00e Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 24 Jun 2025 19:43:46 +0200 Subject: [PATCH 102/138] implement generate surface for ores --- .../engine/actuator/IrisTerrainNormalActuator.java | 14 +++++++++++--- .../com/volmit/iris/engine/object/IrisBiome.java | 4 +++- .../volmit/iris/engine/object/IrisDimension.java | 4 +++- .../com/volmit/iris/engine/object/IrisRegion.java | 4 +++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainNormalActuator.java b/core/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainNormalActuator.java index 7d602021e..646ca0773 100644 --- a/core/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainNormalActuator.java +++ b/core/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainNormalActuator.java @@ -106,6 +106,14 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator } } + BlockData ore = biome.generateOres(realX, i, realZ, rng, getData(), true); + ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData(), true) : ore; + ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData(), true) : ore; + if (ore != null) { + h.set(xf, i, zf, ore); + continue; + } + if (i > he && i <= hf) { fdepth = hf - i; @@ -138,9 +146,9 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator continue; } - BlockData ore = biome.generateOres(realX, i, realZ, rng, getData()); - ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData()) : ore; - ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData()) : ore; + ore = biome.generateOres(realX, i, realZ, rng, getData(), false); + ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData(), false) : ore; + ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData(), false) : ore; if (ore != null) { h.set(xf, i, zf, ore); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisBiome.java b/core/src/main/java/com/volmit/iris/engine/object/IrisBiome.java index 0b4eb11c3..ac1390d83 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisBiome.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisBiome.java @@ -171,12 +171,14 @@ public class IrisBiome extends IrisRegistrant implements IRare { @ArrayType(type = IrisOreGenerator.class, min = 1) private KList ores = new KList<>(); - public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data) { + public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { if (ores.isEmpty()) { return null; } BlockData b = null; for (IrisOreGenerator i : ores) { + if (i.isGenerateSurface() != surface) + continue; b = i.generate(x, y, z, rng, data); if (b != null) { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java index f2c5b724c..0459e1d06 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java @@ -253,12 +253,14 @@ public class IrisDimension extends IrisRegistrant { return (int) getDimensionHeight().getMin(); } - public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data) { + public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { if (ores.isEmpty()) { return null; } BlockData b = null; for (IrisOreGenerator i : ores) { + if (i.isGenerateSurface() != surface) + continue; b = i.generate(x, y, z, rng, data); if (b != null) { diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisRegion.java b/core/src/main/java/com/volmit/iris/engine/object/IrisRegion.java index 158f04f3e..a2712786a 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisRegion.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisRegion.java @@ -151,12 +151,14 @@ public class IrisRegion extends IrisRegistrant implements IRare { @ArrayType(type = IrisOreGenerator.class, min = 1) private KList ores = new KList<>(); - public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data) { + public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { if (ores.isEmpty()) { return null; } BlockData b = null; for (IrisOreGenerator i : ores) { + if (i.isGenerateSurface() != surface) + continue; b = i.generate(x, y, z, rng, data); if (b != null) { From 0ae1334a57fba81752794a0bef28b2d6992e7902 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 27 Jun 2025 12:20:25 +0200 Subject: [PATCH 103/138] fix snippet tab completion --- .../iris/core/project/SchemaBuilder.java | 70 +++++++++---------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java index 61d626247..2e133f944 100644 --- a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java +++ b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java @@ -153,19 +153,7 @@ public class SchemaBuilder { o.put("properties", properties); - if (c.isAnnotationPresent(Snippet.class)) { - JSONObject anyOf = new JSONObject(); - JSONArray arr = new JSONArray(); - JSONObject str = new JSONObject(); - str.put("type", "string"); - arr.put(o); - arr.put(str); - anyOf.put("anyOf", arr); - - return anyOf; - } - - return o; + return buildSnippet(o, c); } private JSONObject buildProperty(Field k, Class cl) { @@ -515,6 +503,13 @@ public class SchemaBuilder { d.add(getDescription(k.getType())); Snippet snippet = k.getType().getDeclaredAnnotation(Snippet.class); + if (snippet == null) { + ArrayType array = k.getType().getDeclaredAnnotation(ArrayType.class); + if (array != null) { + snippet = array.type().getDeclaredAnnotation(Snippet.class); + } + } + if (snippet != null) { String sm = snippet.value(); d.add(" "); @@ -544,35 +539,36 @@ public class SchemaBuilder { description.forEach((g) -> d.add(g.trim())); prop.put("type", type); prop.put("description", d.toString("\n")); + return buildSnippet(prop, k.getType()); + } - if (k.getType().isAnnotationPresent(Snippet.class)) { - JSONObject anyOf = new JSONObject(); - JSONArray arr = new JSONArray(); - JSONObject str = new JSONObject(); - str.put("type", "string"); - String key = "enum-snippet-" + k.getType().getDeclaredAnnotation(Snippet.class).value(); - str.put("$ref", "#/definitions/" + key); + private JSONObject buildSnippet(JSONObject prop, Class type) { + Snippet snippet = type.getDeclaredAnnotation(Snippet.class); + if (snippet == null) return prop; - if (!definitions.containsKey(key)) { - JSONObject j = new JSONObject(); - JSONArray snl = new JSONArray(); - data.getPossibleSnippets(k.getType().getDeclaredAnnotation(Snippet.class).value()).forEach(snl::put); - j.put("enum", snl); - definitions.put(key, j); - } + JSONObject anyOf = new JSONObject(); + JSONArray arr = new JSONArray(); + JSONObject str = new JSONObject(); + str.put("type", "string"); + String key = "enum-snippet-" + snippet.value(); + str.put("$ref", "#/definitions/" + key); - arr.put(prop); - arr.put(str); - prop.put("description", d.toString("\n")); - str.put("description", d.toString("\n")); - anyOf.put("anyOf", arr); - anyOf.put("description", d.toString("\n")); - anyOf.put("!required", k.isAnnotationPresent(Required.class)); - - return anyOf; + if (!definitions.containsKey(key)) { + JSONObject j = new JSONObject(); + JSONArray snl = new JSONArray(); + data.getPossibleSnippets(snippet.value()).forEach(snl::put); + j.put("enum", snl); + definitions.put(key, j); } - return prop; + arr.put(prop); + arr.put(str); + str.put("description", prop.getString("description")); + anyOf.put("anyOf", arr); + anyOf.put("description", prop.getString("description")); + anyOf.put("!required", type.isAnnotationPresent(Required.class)); + + return anyOf; } @NotNull From 6f0b2b6bba3a12c5de46b721f12cb8f1c594def4 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 27 Jun 2025 12:21:59 +0200 Subject: [PATCH 104/138] initialize generators for isolated focus biomes / regions --- .../com/volmit/iris/engine/IrisComplex.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/IrisComplex.java b/core/src/main/java/com/volmit/iris/engine/IrisComplex.java index e92e6da84..652b728ca 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisComplex.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisComplex.java @@ -108,10 +108,17 @@ public class IrisComplex implements DataProvider { } //@builder - engine.getDimension().getRegions().forEach((i) -> data.getRegionLoader().load(i) - .getAllBiomes(this).forEach((b) -> b - .getGenerators() - .forEach((c) -> registerGenerator(c.getCachedGenerator(this))))); + if (focusRegion != null) { + focusRegion.getAllBiomes(this).forEach(this::registerGenerators); + } else if (focusBiome != null) { + registerGenerators(focusBiome); + } else { + engine.getDimension() + .getRegions() + .forEach(i -> data.getRegionLoader().load(i) + .getAllBiomes(this) + .forEach(this::registerGenerators)); + } overlayStream = ProceduralStream.ofDouble((x, z) -> 0.0D).waste("Overlay Stream"); engine.getDimension().getOverlayNoise().forEach(i -> overlayStream = overlayStream.add((x, z) -> i.get(rng, getData(), x, z))); rockStream = engine.getDimension().getRockPalette().getLayerGenerator(rng.nextParallelRNG(45), data).stream() @@ -360,6 +367,10 @@ public class IrisComplex implements DataProvider { return Math.max(Math.min(getInterpolatedHeight(engine, x, z, seed) + fluidHeight + overlayStream.get(x, z), engine.getHeight()), 0); } + private void registerGenerators(IrisBiome biome) { + biome.getGenerators().forEach(c -> registerGenerator(c.getCachedGenerator(this))); + } + private void registerGenerator(IrisGenerator cachedGenerator) { generators.computeIfAbsent(cachedGenerator.getInterpolator(), (k) -> new KSet<>()).add(cachedGenerator); } From 8c025a9ba27eddccc2b64302a89772d7ba7b31e5 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Thu, 3 Jul 2025 23:45:27 +0200 Subject: [PATCH 105/138] clear snippet lists on hotload --- core/src/main/java/com/volmit/iris/core/loader/IrisData.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java index 52144fef5..e6d410a5b 100644 --- a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java +++ b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java @@ -363,6 +363,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { for (ResourceLoader i : loaders.values()) { i.clearList(); } + possibleSnippets.clear(); } public String toLoadKey(File f) { From 39f65d02bfff812d37af61207ae0d56a5f01ee9d Mon Sep 17 00:00:00 2001 From: Julian Krings <47589149+CrazyDev05@users.noreply.github.com> Date: Thu, 3 Jul 2025 23:58:31 +0200 Subject: [PATCH 106/138] 1.21.6/7 Support (#1210) --- build.gradle.kts | 1 + .../volmit/iris/core/ServerConfigurator.java | 4 + .../java/com/volmit/iris/core/nms/INMS.java | 4 +- .../iris/core/nms/datapack/DataVersion.java | 1 + .../iris/core/nms/v1X/NMSBinding1X.java | 6 + .../decree/handlers/DataVersionHandler.java | 2 +- .../core/nms/v1_21_R5/CustomBiomeSource.java | 169 ++++ .../core/nms/v1_21_R5/IrisChunkGenerator.java | 311 +++++++ .../iris/core/nms/v1_21_R5/NMSBinding.java | 762 ++++++++++++++++++ settings.gradle.kts | 1 + 10 files changed, 1259 insertions(+), 2 deletions(-) create mode 100644 nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/CustomBiomeSource.java create mode 100644 nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/IrisChunkGenerator.java create mode 100644 nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/NMSBinding.java diff --git a/build.gradle.kts b/build.gradle.kts index 98ee16ae3..2fc7ed8b5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -63,6 +63,7 @@ val color = "truecolor" val errorReporting = false val nmsBindings = mapOf( + "v1_21_R5" to "1.21.7-R0.1-SNAPSHOT", "v1_21_R4" to "1.21.5-R0.1-SNAPSHOT", "v1_21_R3" to "1.21.4-R0.1-SNAPSHOT", "v1_21_R2" to "1.21.3-R0.1-SNAPSHOT", diff --git a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java index 79261d63d..ed833b323 100644 --- a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java +++ b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java @@ -107,6 +107,10 @@ public class ServerConfigurator { } public static void installDataPacks(IDataFixer fixer, boolean fullInstall) { + if (fixer == null) { + Iris.error("Unable to install datapacks, fixer is null!"); + return; + } Iris.info("Checking Data Packs..."); DimensionHeight height = new DimensionHeight(fixer); KList folders = getDatapacksFolder(); diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMS.java b/core/src/main/java/com/volmit/iris/core/nms/INMS.java index 99ef8514f..848b08879 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMS.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMS.java @@ -35,7 +35,9 @@ public class INMS { "1.21.2", "v1_21_R2", "1.21.3", "v1_21_R2", "1.21.4", "v1_21_R3", - "1.21.5", "v1_21_R4" + "1.21.5", "v1_21_R4", + "1.21.6", "v1_21_R5", + "1.21.7", "v1_21_R5" ); private static final List PACKS = List.of( new Version(21, 4, "31020"), diff --git a/core/src/main/java/com/volmit/iris/core/nms/datapack/DataVersion.java b/core/src/main/java/com/volmit/iris/core/nms/datapack/DataVersion.java index f73850b0c..1314d8ecf 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/datapack/DataVersion.java +++ b/core/src/main/java/com/volmit/iris/core/nms/datapack/DataVersion.java @@ -13,6 +13,7 @@ import java.util.function.Supplier; //https://minecraft.wiki/w/Pack_format @Getter public enum DataVersion { + UNSUPPORTED("0.0.0", 0, () -> null), V1192("1.19.2", 10, DataFixerV1192::new), V1205("1.20.6", 41, DataFixerV1206::new), V1213("1.21.3", 57, DataFixerV1213::new); diff --git a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java index 642a00e38..e180ab8d2 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java +++ b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java @@ -21,6 +21,7 @@ package com.volmit.iris.core.nms.v1X; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; @@ -207,6 +208,11 @@ public class NMSBinding1X implements INMSBinding { return true; } + @Override + public DataVersion getDataVersion() { + return DataVersion.UNSUPPORTED; + } + @Override public int getBiomeId(Biome biome) { return biome.ordinal(); diff --git a/core/src/main/java/com/volmit/iris/util/decree/handlers/DataVersionHandler.java b/core/src/main/java/com/volmit/iris/util/decree/handlers/DataVersionHandler.java index 489590496..53dff488e 100644 --- a/core/src/main/java/com/volmit/iris/util/decree/handlers/DataVersionHandler.java +++ b/core/src/main/java/com/volmit/iris/util/decree/handlers/DataVersionHandler.java @@ -8,7 +8,7 @@ import com.volmit.iris.util.decree.exceptions.DecreeParsingException; public class DataVersionHandler implements DecreeParameterHandler { @Override public KList getPossibilities() { - return new KList<>(DataVersion.values()); + return new KList<>(DataVersion.values()).qdel(DataVersion.UNSUPPORTED); } @Override diff --git a/nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/CustomBiomeSource.java b/nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/CustomBiomeSource.java new file mode 100644 index 000000000..c8fa95353 --- /dev/null +++ b/nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/CustomBiomeSource.java @@ -0,0 +1,169 @@ +package com.volmit.iris.core.nms.v1_21_R5; + +import com.mojang.serialization.MapCodec; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.object.IrisBiome; +import com.volmit.iris.engine.object.IrisBiomeCustom; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.math.RNG; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.Climate; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_21_R5.CraftServer; +import org.bukkit.craftbukkit.v1_21_R5.CraftWorld; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +public class CustomBiomeSource extends BiomeSource { + + private final long seed; + private final Engine engine; + private final Registry biomeCustomRegistry; + private final Registry biomeRegistry; + private final AtomicCache registryAccess = new AtomicCache<>(); + private final RNG rng; + private final KMap> customBiomes; + + public CustomBiomeSource(long seed, Engine engine, World world) { + this.engine = engine; + this.seed = seed; + this.biomeCustomRegistry = registry().lookup(Registries.BIOME).orElse(null); + this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).lookup(Registries.BIOME).orElse(null); + this.rng = new RNG(engine.getSeedManager().getBiome()); + this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine); + } + + private static List> getAllBiomes(Registry customRegistry, Registry registry, Engine engine) { + List> b = new ArrayList<>(); + + for (IrisBiome i : engine.getAllBiomes()) { + if (i.isCustom()) { + for (IrisBiomeCustom j : i.getCustomDerivitives()) { + b.add(customRegistry.get(customRegistry.getResourceKey(customRegistry + .getValue(ResourceLocation.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId()))).get()).get()); + } + } else { + b.add(NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative())); + } + } + + return b; + } + + private static Object getFor(Class type, Object source) { + Object o = fieldFor(type, source); + + if (o != null) { + return o; + } + + return invokeFor(type, source); + } + + private static Object fieldFor(Class returns, Object in) { + return fieldForClass(returns, in.getClass(), in); + } + + private static Object invokeFor(Class returns, Object in) { + for (Method i : in.getClass().getMethods()) { + if (i.getReturnType().equals(returns)) { + i.setAccessible(true); + try { + Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()"); + return i.invoke(in); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + return null; + } + + @SuppressWarnings("unchecked") + private static T fieldForClass(Class returnType, Class sourceType, Object in) { + for (Field i : sourceType.getDeclaredFields()) { + if (i.getType().equals(returnType)) { + i.setAccessible(true); + try { + Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName()); + return (T) i.get(in); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + return null; + } + + @Override + protected Stream> collectPossibleBiomes() { + return getAllBiomes( + ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())) + .lookup(Registries.BIOME).orElse(null), + ((CraftWorld) engine.getWorld().realWorld()).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null), + engine).stream(); + } + private KMap> fillCustomBiomes(Registry customRegistry, Engine engine) { + KMap> m = new KMap<>(); + + for (IrisBiome i : engine.getAllBiomes()) { + if (i.isCustom()) { + for (IrisBiomeCustom j : i.getCustomDerivitives()) { + ResourceLocation resourceLocation = ResourceLocation.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId()); + Biome biome = customRegistry.getValue(resourceLocation); + Optional> optionalBiomeKey = customRegistry.getResourceKey(biome); + if (optionalBiomeKey.isEmpty()) { + Iris.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName()); + continue; + } + ResourceKey biomeKey = optionalBiomeKey.get(); + Optional> optionalReferenceHolder = customRegistry.get(biomeKey); + if (optionalReferenceHolder.isEmpty()) { + Iris.error("Cannot find reference to biome " + biomeKey + " for engine " + engine.getName()); + continue; + } + m.put(j.getId(), optionalReferenceHolder.get()); + } + } + } + + return m; + } + + private RegistryAccess registry() { + return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())); + } + + @Override + protected MapCodec codec() { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { + int m = (y - engine.getMinHeight()) << 2; + IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2); + if (ib.isCustom()) { + return customBiomes.get(ib.getCustomBiome(rng, x << 2, m, z << 2).getId()); + } else { + org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2); + return NMSBinding.biomeToBiomeBase(biomeRegistry, v); + } + } +} \ No newline at end of file diff --git a/nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/IrisChunkGenerator.java b/nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/IrisChunkGenerator.java new file mode 100644 index 000000000..c156c09df --- /dev/null +++ b/nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/IrisChunkGenerator.java @@ -0,0 +1,311 @@ +package com.volmit.iris.core.nms.v1_21_R5; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.MapCodec; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.ResultLocator; +import com.volmit.iris.engine.framework.WrongEngineBroException; +import com.volmit.iris.engine.object.IrisJigsawStructure; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.math.Position2; +import com.volmit.iris.util.reflect.WrappedField; +import net.minecraft.core.*; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.StructureTags; +import net.minecraft.tags.TagKey; +import net.minecraft.util.random.WeightedList; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.*; +import net.minecraft.world.level.biome.*; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.StructureSet; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_21_R5.CraftWorld; +import org.bukkit.craftbukkit.v1_21_R5.generator.CustomChunkGenerator; +import org.spigotmc.SpigotWorldConfig; + +import javax.annotation.Nullable; +import java.lang.reflect.Field; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class IrisChunkGenerator extends CustomChunkGenerator { + private static final WrappedField BIOME_SOURCE; + private final ChunkGenerator delegate; + private final Engine engine; + private final KMap, KSet> structures = new KMap<>(); + + public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { + super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null); + this.delegate = delegate; + this.engine = engine; + var dimension = engine.getDimension(); + + KSet placements = new KSet<>(); + addAll(dimension.getJigsawStructures(), placements); + for (var region : dimension.getAllRegions(engine)) { + addAll(region.getJigsawStructures(), placements); + for (var biome : region.getAllBiomes(engine)) + addAll(biome.getJigsawStructures(), placements); + } + var stronghold = dimension.getStronghold(); + if (stronghold != null) + placements.add(engine.getData().getJigsawStructureLoader().load(stronghold)); + placements.removeIf(Objects::isNull); + + var registry = ((CraftWorld) world).getHandle().registryAccess().lookup(Registries.STRUCTURE).orElseThrow(); + for (var s : placements) { + try { + String raw = s.getStructureKey(); + if (raw == null) continue; + boolean tag = raw.startsWith("#"); + if (tag) raw = raw.substring(1); + + var location = ResourceLocation.parse(raw); + if (!tag) { + structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey()); + continue; + } + + var key = TagKey.create(Registries.STRUCTURE, location); + var set = registry.get(key).orElse(null); + if (set == null) { + Iris.error("Could not find structure tag: " + raw); + continue; + } + for (var holder : set) { + var resourceKey = holder.unwrapKey().orElse(null); + if (resourceKey == null) continue; + structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey()); + } + } catch (Throwable e) { + Iris.error("Failed to load structure: " + s.getLoadKey()); + e.printStackTrace(); + } + } + } + + private void addAll(KList placements, KSet structures) { + if (placements == null) return; + placements.stream() + .map(IrisJigsawStructurePlacement::getStructure) + .map(engine.getData().getJigsawStructureLoader()::load) + .filter(Objects::nonNull) + .forEach(structures::add); + } + + @Override + public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (holders.size() == 0) return null; + if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) { + var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ())); + return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0)); + } + if (engine.getDimension().isDisableExplorerMaps()) + return null; + + KMap> structures = new KMap<>(); + for (var holder : holders) { + if (holder == null) continue; + var key = holder.unwrapKey().orElse(null); + var set = this.structures.get(key); + if (set == null) continue; + for (var structure : set) { + structures.put(structure, holder); + } + } + if (structures.isEmpty()) + return null; + + var locator = ResultLocator.locateStructure(structures.keySet()) + .then((e, p , s) -> structures.get(s.getLoadKey())); + if (findUnexplored) + locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s); + + try { + var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get(); + if (result == null) return null; + var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ()); + return Pair.of(blockPos, result.obj()); + } catch (WrongEngineBroException | ExecutionException | InterruptedException e) { + return null; + } + } + + @Override + protected MapCodec codec() { + return MapCodec.unit(null); + } + + @Override + public ChunkGenerator getDelegate() { + if (delegate instanceof CustomChunkGenerator chunkGenerator) + return chunkGenerator.getDelegate(); + return delegate; + } + + @Override + public int getMinY() { + return delegate.getMinY(); + } + + @Override + public int getSeaLevel() { + return delegate.getSeaLevel(); + } + + @Override + public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey resourcekey) { + delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey); + } + + @Override + public ChunkGeneratorStructureState createState(HolderLookup holderlookup, RandomState randomstate, long i, SpigotWorldConfig conf) { + return delegate.createState(holderlookup, randomstate, i, conf); + } + + @Override + public void createReferences(WorldGenLevel generatoraccessseed, StructureManager structuremanager, ChunkAccess ichunkaccess) { + delegate.createReferences(generatoraccessseed, structuremanager, ichunkaccess); + } + + @Override + public CompletableFuture createBiomes(RandomState randomstate, Blender blender, StructureManager structuremanager, ChunkAccess ichunkaccess) { + return delegate.createBiomes(randomstate, blender, structuremanager, ichunkaccess); + } + + @Override + public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) { + delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess); + } + + @Override + public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess) { + delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess); + } + + @Override + public CompletableFuture fillFromNoise(Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) { + return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess); + } + + @Override + public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public WeightedList getMobsAt(Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { + return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition); + } + + @Override + public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager); + } + + @Override + public void addDebugScreenInfo(List list, RandomState randomstate, BlockPos blockposition) { + delegate.addDebugScreenInfo(list, randomstate, blockposition); + } + + @Override + public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) { + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, vanilla); + } + + @Override + public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public void addVanillaDecorations(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { + delegate.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager); + } + + @Override + public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) { + delegate.spawnOriginalMobs(regionlimitedworldaccess); + } + + @Override + public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) { + return delegate.getSpawnHeight(levelheightaccessor); + } + + @Override + public int getGenDepth() { + return delegate.getGenDepth(); + } + + @Override + public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate); + } + + @Override + public Optional>> getTypeNameForDataFixer() { + return delegate.getTypeNameForDataFixer(); + } + + @Override + public void validate() { + delegate.validate(); + } + + @Override + @SuppressWarnings("deprecation") + public BiomeGenerationSettings getBiomeGenerationSettings(Holder holder) { + return delegate.getBiomeGenerationSettings(holder); + } + + static { + Field biomeSource = null; + for (Field field : ChunkGenerator.class.getDeclaredFields()) { + if (!field.getType().equals(BiomeSource.class)) + continue; + biomeSource = field; + break; + } + if (biomeSource == null) + throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!"); + BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName()); + } + + private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) { + try { + BIOME_SOURCE.set(generator, source); + if (generator instanceof CustomChunkGenerator custom) + BIOME_SOURCE.set(custom.getDelegate(), source); + + return generator; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } +} diff --git a/nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/NMSBinding.java b/nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/NMSBinding.java new file mode 100644 index 000000000..425dd25a7 --- /dev/null +++ b/nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/NMSBinding.java @@ -0,0 +1,762 @@ +package com.volmit.iris.core.nms.v1_21_R5; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.volmit.iris.Iris; +import com.volmit.iris.core.nms.INMSBinding; +import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.platform.PlatformChunkGenerator; +import com.volmit.iris.util.agent.Agent; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.hunk.Hunk; +import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.math.Vector3d; +import com.volmit.iris.util.matter.MatterBiomeInject; +import com.volmit.iris.util.nbt.mca.NBTWorld; +import com.volmit.iris.util.nbt.mca.palette.*; +import com.volmit.iris.util.nbt.tag.CompoundTag; +import com.volmit.iris.util.scheduling.J; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.shorts.ShortList; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.matcher.ElementMatchers; +import net.minecraft.core.*; +import net.minecraft.core.Registry; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.*; +import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.commands.data.BlockDataAccessor; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.tags.TagKey; +import net.minecraft.world.RandomSequences; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.ProtoChunk; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import net.minecraft.world.level.chunk.status.WorldGenContext; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.levelgen.FlatLevelSource; +import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; +import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; +import org.bukkit.*; +import org.bukkit.block.Biome; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.v1_21_R5.CraftChunk; +import org.bukkit.craftbukkit.v1_21_R5.CraftServer; +import org.bukkit.craftbukkit.v1_21_R5.CraftWorld; +import org.bukkit.craftbukkit.v1_21_R5.block.CraftBlockState; +import org.bukkit.craftbukkit.v1_21_R5.block.CraftBlockStates; +import org.bukkit.craftbukkit.v1_21_R5.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_21_R5.util.CraftNamespacedKey; +import org.bukkit.entity.Entity; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.BiomeProvider; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.awt.Color; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +public class NMSBinding implements INMSBinding { + private final KMap baseBiomeCache = new KMap<>(); + private final BlockData AIR = Material.AIR.createBlockData(); + private final AtomicCache> biomeMapCache = new AtomicCache<>(); + private final AtomicBoolean injected = new AtomicBoolean(); + private final AtomicCache> registryCache = new AtomicCache<>(); + private final AtomicCache> globalCache = new AtomicCache<>(); + private final AtomicCache registryAccess = new AtomicCache<>(); + private final AtomicCache byIdRef = new AtomicCache<>(); + private Field biomeStorageCache = null; + + private static Object getFor(Class type, Object source) { + Object o = fieldFor(type, source); + + if (o != null) { + return o; + } + + return invokeFor(type, source); + } + + private static Object invokeFor(Class returns, Object in) { + for (Method i : in.getClass().getMethods()) { + if (i.getReturnType().equals(returns)) { + i.setAccessible(true); + try { + Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()"); + return i.invoke(in); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + return null; + } + + private static Object fieldFor(Class returns, Object in) { + return fieldForClass(returns, in.getClass(), in); + } + + @SuppressWarnings("unchecked") + private static T fieldForClass(Class returnType, Class sourceType, Object in) { + for (Field i : sourceType.getDeclaredFields()) { + if (i.getType().equals(returnType)) { + i.setAccessible(true); + try { + Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName()); + return (T) i.get(in); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + return null; + } + + private static Class getClassType(Class type, int ordinal) { + return type.getDeclaredClasses()[ordinal]; + } + + @Override + public boolean hasTile(Material material) { + return !CraftBlockState.class.equals(CraftBlockStates.getBlockStateType(material)); + } + + @Override + public boolean hasTile(Location l) { + return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ()), false) != null; + } + + @Override + @SuppressWarnings("unchecked") + public KMap serializeTile(Location location) { + BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), false); + + if (e == null) { + return null; + } + + net.minecraft.nbt.CompoundTag tag = e.saveWithoutMetadata(registry()); + return (KMap) convertFromTag(tag, 0, 64); + } + + @Contract(value = "null, _, _ -> null", pure = true) + private Object convertFromTag(Tag tag, int depth, int maxDepth) { + if (tag == null || depth > maxDepth) return null; + return switch (tag) { + case CollectionTag collection -> { + KList list = new KList<>(); + + for (Object i : collection) { + if (i instanceof Tag t) + list.add(convertFromTag(t, depth + 1, maxDepth)); + else list.add(i); + } + yield list; + } + case net.minecraft.nbt.CompoundTag compound -> { + KMap map = new KMap<>(); + + for (String key : compound.keySet()) { + var child = compound.get(key); + if (child == null) continue; + var value = convertFromTag(child, depth + 1, maxDepth); + if (value == null) continue; + map.put(key, value); + } + yield map; + } + case NumericTag numeric -> numeric.box(); + default -> tag.asString().orElse(null); + }; + } + + @Override + public void deserializeTile(KMap map, Location pos) { + net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) convertToTag(map, 0, 64); + var level = ((CraftWorld) pos.getWorld()).getHandle(); + var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()); + J.s(() -> merge(level, blockPos, tag)); + } + + private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) { + var blockEntity = level.getBlockEntity(blockPos); + if (blockEntity == null) { + Iris.warn("[NMS] BlockEntity not found at " + blockPos); + var state = level.getBlockState(blockPos); + if (!state.hasBlockEntity()) + return; + + blockEntity = ((EntityBlock) state.getBlock()) + .newBlockEntity(blockPos, state); + } + var accessor = new BlockDataAccessor(blockEntity, blockPos); + accessor.setData(accessor.getData().merge(tag)); + } + + private Tag convertToTag(Object object, int depth, int maxDepth) { + if (object == null || depth > maxDepth) return EndTag.INSTANCE; + return switch (object) { + case Map map -> { + var tag = new net.minecraft.nbt.CompoundTag(); + for (var i : map.entrySet()) { + tag.put(i.getKey().toString(), convertToTag(i.getValue(), depth + 1, maxDepth)); + } + yield tag; + } + case List list -> { + var tag = new ListTag(); + for (var i : list) { + tag.add(convertToTag(i, depth + 1, maxDepth)); + } + yield tag; + } + case Byte number -> ByteTag.valueOf(number); + case Short number -> ShortTag.valueOf(number); + case Integer number -> IntTag.valueOf(number); + case Long number -> LongTag.valueOf(number); + case Float number -> FloatTag.valueOf(number); + case Double number -> DoubleTag.valueOf(number); + case String string -> StringTag.valueOf(string); + default -> EndTag.INSTANCE; + }; + } + + @Override + public CompoundTag serializeEntity(Entity location) { + return null;// TODO: + } + + @Override + public Entity deserializeEntity(CompoundTag s, Location newPosition) { + return null;// TODO: + } + + @Override + public boolean supportsCustomHeight() { + return true; + } + + private RegistryAccess registry() { + return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())); + } + + private Registry getCustomBiomeRegistry() { + return registry().lookup(Registries.BIOME).orElse(null); + } + + private Registry getBlockRegistry() { + return registry().lookup(Registries.BLOCK).orElse(null); + } + + @Override + public Object getBiomeBaseFromId(int id) { + return getCustomBiomeRegistry().get(id); + } + + @Override + public int getMinHeight(World world) { + return world.getMinHeight(); + } + + @Override + public boolean supportsCustomBiomes() { + return true; + } + + @Override + public int getTrueBiomeBaseId(Object biomeBase) { + return getCustomBiomeRegistry().getId(((Holder) biomeBase).value()); + } + + @Override + public Object getTrueBiomeBase(Location location) { + return ((CraftWorld) location.getWorld()).getHandle().getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ())); + } + + @Override + public String getTrueBiomeBaseKey(Location location) { + return getKeyForBiomeBase(getTrueBiomeBase(location)); + } + + @Override + public Object getCustomBiomeBaseFor(String mckey) { + return getCustomBiomeRegistry().getValue(ResourceLocation.parse(mckey)); + } + + @Override + public Object getCustomBiomeBaseHolderFor(String mckey) { + return getCustomBiomeRegistry().get(getTrueBiomeBaseId(getCustomBiomeRegistry().get(ResourceLocation.parse(mckey)))).orElse(null); + } + + public int getBiomeBaseIdForKey(String key) { + return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(ResourceLocation.parse(key)).map(Holder::value).orElse(null)); + } + + @Override + public String getKeyForBiomeBase(Object biomeBase) { + return getCustomBiomeRegistry().getKey((net.minecraft.world.level.biome.Biome) biomeBase).getPath(); // something, not something:something + } + + @Override + public Object getBiomeBase(World world, Biome biome) { + return biomeToBiomeBase(((CraftWorld) world).getHandle() + .registryAccess().lookup(Registries.BIOME).orElse(null), biome); + } + + @Override + public Object getBiomeBase(Object registry, Biome biome) { + Object v = baseBiomeCache.get(biome); + + if (v != null) { + return v; + } + //noinspection unchecked + v = biomeToBiomeBase((Registry) registry, biome); + if (v == null) { + // Ok so there is this new biome name called "CUSTOM" in Paper's new releases. + // But, this does NOT exist within CraftBukkit which makes it return an error. + // So, we will just return the ID that the plains biome returns instead. + //noinspection unchecked + return biomeToBiomeBase((Registry) registry, Biome.PLAINS); + } + baseBiomeCache.put(biome, v); + return v; + } + + @Override + public KList getBiomes() { + return new KList<>(Biome.values()).qadd(Biome.CHERRY_GROVE).qdel(Biome.CUSTOM); + } + + @Override + public boolean isBukkit() { + return true; + } + + @Override + public int getBiomeId(Biome biome) { + for (World i : Bukkit.getWorlds()) { + if (i.getEnvironment().equals(World.Environment.NORMAL)) { + Registry registry = ((CraftWorld) i).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null); + return registry.getId((net.minecraft.world.level.biome.Biome) getBiomeBase(registry, biome)); + } + } + + return biome.ordinal(); + } + + private MCAIdMap getBiomeMapping() { + return biomeMapCache.aquire(() -> new MCAIdMap<>() { + @NotNull + @Override + public Iterator iterator() { + return getCustomBiomeRegistry().iterator(); + } + + @Override + public int getId(net.minecraft.world.level.biome.Biome paramT) { + return getCustomBiomeRegistry().getId(paramT); + } + + @Override + public net.minecraft.world.level.biome.Biome byId(int paramInt) { + return (net.minecraft.world.level.biome.Biome) getBiomeBaseFromId(paramInt); + } + }); + } + + @NotNull + private MCABiomeContainer getBiomeContainerInterface(MCAIdMap biomeMapping, MCAChunkBiomeContainer base) { + return new MCABiomeContainer() { + @Override + public int[] getData() { + return base.writeBiomes(); + } + + @Override + public void setBiome(int x, int y, int z, int id) { + base.setBiome(x, y, z, biomeMapping.byId(id)); + } + + @Override + public int getBiome(int x, int y, int z) { + return biomeMapping.getId(base.getBiome(x, y, z)); + } + }; + } + + @Override + public MCABiomeContainer newBiomeContainer(int min, int max) { + MCAChunkBiomeContainer base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max); + return getBiomeContainerInterface(getBiomeMapping(), base); + } + + @Override + public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) { + MCAChunkBiomeContainer base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max, data); + return getBiomeContainerInterface(getBiomeMapping(), base); + } + + @Override + public int countCustomBiomes() { + AtomicInteger a = new AtomicInteger(0); + + getCustomBiomeRegistry().keySet().forEach((i) -> { + if (i.getNamespace().equals("minecraft")) { + return; + } + + a.incrementAndGet(); + Iris.debug("Custom Biome: " + i); + }); + + return a.get(); + } + + public boolean supportsDataPacks() { + return true; + } + + public void setBiomes(int cx, int cz, World world, Hunk biomes) { + LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz); + biomes.iterateSync((x, y, z, b) -> c.setBiome(x, y, z, (Holder) b)); + c.markUnsaved(); + } + + @Override + public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) { + try { + ChunkAccess s = (ChunkAccess) getFieldForBiomeStorage(chunk).get(chunk); + Holder biome = (Holder) somethingVeryDirty; + s.setBiome(x, y, z, biome); + } catch (IllegalAccessException e) { + Iris.reportError(e); + e.printStackTrace(); + } + } + + private Field getFieldForBiomeStorage(Object storage) { + Field f = biomeStorageCache; + + if (f != null) { + return f; + } + try { + f = storage.getClass().getDeclaredField("biome"); + f.setAccessible(true); + return f; + } catch (Throwable e) { + Iris.reportError(e); + e.printStackTrace(); + Iris.error(storage.getClass().getCanonicalName()); + } + + biomeStorageCache = f; + return null; + } + + @Override + public MCAPaletteAccess createPalette() { + MCAIdMapper registry = registryCache.aquireNasty(() -> { + Field cf = IdMapper.class.getDeclaredField("tToId"); + Field df = IdMapper.class.getDeclaredField("idToT"); + Field bf = IdMapper.class.getDeclaredField("nextId"); + cf.setAccessible(true); + df.setAccessible(true); + bf.setAccessible(true); + IdMapper blockData = Block.BLOCK_STATE_REGISTRY; + int b = bf.getInt(blockData); + Object2IntMap c = (Object2IntMap) cf.get(blockData); + List d = (List) df.get(blockData); + return new MCAIdMapper(c, d, b); + }); + MCAPalette global = globalCache.aquireNasty(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState())); + MCAPalettedContainer container = new MCAPalettedContainer<>(global, registry, + i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState(), + i -> NBTWorld.getCompound(CraftBlockData.fromData(i)), + ((CraftBlockData) AIR).getState()); + return new MCAWrappedPalettedContainer<>(container, + i -> NBTWorld.getCompound(CraftBlockData.fromData(i)), + i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState()); + } + + @Override + public void injectBiomesFromMantle(Chunk e, Mantle mantle) { + ChunkAccess chunk = ((CraftChunk) e).getHandle(ChunkStatus.FULL); + AtomicInteger c = new AtomicInteger(); + AtomicInteger r = new AtomicInteger(); + mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x, y, z, b) -> { + if (b != null) { + if (b.isCustom()) { + chunk.setBiome(x, y, z, getCustomBiomeRegistry().get(b.getBiomeId()).get()); + c.getAndIncrement(); + } else { + chunk.setBiome(x, y, z, (Holder) getBiomeBase(e.getWorld(), b.getBiome())); + r.getAndIncrement(); + } + } + }); + } + + public ItemStack applyCustomNbt(ItemStack itemStack, KMap customNbt) throws IllegalArgumentException { + if (customNbt != null && !customNbt.isEmpty()) { + net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack); + + try { + net.minecraft.nbt.CompoundTag tag = TagParser.parseCompoundFully((new JSONObject(customNbt)).toString()); + tag.merge(s.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).getUnsafe()); + s.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + } catch (CommandSyntaxException var5) { + throw new IllegalArgumentException(var5); + } + + return CraftItemStack.asBukkitCopy(s); + } else { + return itemStack; + } + } + + public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { + var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap; + var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class); + worldGenContextField.setAccessible(true); + var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap); + var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null); + if (dimensionType != null && !dimensionType.location().getNamespace().equals("iris")) + Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().toString()); + + var newContext = new WorldGenContext( + worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world), + worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadExecutor(), worldGenContext.unsavedListener()); + + worldGenContextField.set(chunkMap, newContext); + } + + public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { + Field[] fields = EntityType.class.getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) { + try { + EntityType entityType = (EntityType) field.get(null); + if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) { + Vector v1 = new Vector<>(); + v1.add(entityType.getHeight()); + entityType.getDimensions(); + Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth()); + //System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width); + return box; + } + } catch (IllegalAccessException e) { + Iris.error("Unable to get entity dimensions!"); + e.printStackTrace(); + } + } + } + return null; + } + + + @Override + public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) { + return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason); + } + + @Override + public Color getBiomeColor(Location location, BiomeColor type) { + LevelReader reader = ((CraftWorld) location.getWorld()).getHandle(); + var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ())); + var biome = holder.value(); + if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null)); + + int rgba = switch (type) { + case FOG -> biome.getFogColor(); + case WATER -> biome.getWaterColor(); + case WATER_FOG -> biome.getWaterFogColor(); + case SKY -> biome.getSkyColor(); + case FOLIAGE -> biome.getFoliageColor(); + case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ()); + }; + if (rgba == 0) { + if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().getFoliageColorOverride().isEmpty()) + return null; + if (BiomeColor.GRASS == type && biome.getSpecialEffects().getGrassColorOverride().isEmpty()) + return null; + } + return new Color(rgba, true); + } + + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { + try { + for (Field f : clazz.getDeclaredFields()) { + if (f.getType().equals(fieldType)) + return f; + } + throw new NoSuchFieldException(fieldType.getName()); + } catch (NoSuchFieldException var4) { + Class superClass = clazz.getSuperclass(); + if (superClass == null) { + throw var4; + } else { + return getField(superClass, fieldType); + } + } + } + + public static Holder biomeToBiomeBase(Registry registry, Biome biome) { + return registry.getOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey()))); + } + + @Override + public DataVersion getDataVersion() { + return DataVersion.V1213; + } + + @Override + public int getSpawnChunkCount(World world) { + var radius = Optional.ofNullable(world.getGameRuleValue(GameRule.SPAWN_CHUNK_RADIUS)) + .orElseGet(() -> world.getGameRuleDefault(GameRule.SPAWN_CHUNK_RADIUS)); + if (radius == null) throw new IllegalStateException("GameRule.SPAWN_CHUNK_RADIUS is null!"); + return (int) Math.pow(2 * radius + 1, 2); + } + + @Override + public KList getStructureKeys() { + KList keys = new KList<>(); + + var registry = registry().lookup(Registries.STRUCTURE).orElse(null); + if (registry == null) return keys; + registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add); + registry.getTags() + .map(HolderSet.Named::key) + .map(TagKey::location) + .map(ResourceLocation::toString) + .map(s -> "#" + s) + .forEach(keys::add); + + return keys; + } + + @Override + public boolean missingDimensionTypes(String... keys) { + var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE); + return !Arrays.stream(keys) + .map(key -> ResourceLocation.fromNamespaceAndPath("iris", key)) + .allMatch(type::containsKey); + } + + @Override + public boolean injectBukkit() { + if (injected.getAndSet(true)) + return true; + try { + Iris.info("Injecting Bukkit"); + var buddy = new ByteBuddy(); + buddy.redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments( + MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, PrimaryLevelData.class, + ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, List.class, + boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), Agent.installed()); + for (Class clazz : List.of(ChunkAccess.class, ProtoChunk.class)) { + buddy.redefine(clazz) + .visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class)))) + .make() + .load(clazz.getClassLoader(), Agent.installed()); + } + + return true; + } catch (Throwable e) { + Iris.error(C.RED + "Failed to inject Bukkit"); + e.printStackTrace(); + } + return false; + } + + public LevelStem levelStem(RegistryAccess access, ChunkGenerator raw) { + if (!(raw instanceof PlatformChunkGenerator gen)) + throw new IllegalStateException("Generator is not platform chunk generator!"); + + var dimensionKey = ResourceLocation.fromNamespaceAndPath("iris", gen.getTarget().getDimension().getDimensionTypeKey()); + var dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE).getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey)); + return new LevelStem(dimensionType, chunkGenerator(access)); + } + + private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { + var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of()); + settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); + settings.updateLayers(); + return new FlatLevelSource(settings); + } + + private static class ChunkAccessAdvice { + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) { + return index >= access.getPostProcessing().length; + } + } + + private static class ServerLevelAdvice { + @Advice.OnMethodEnter + static void enter( + @Advice.Argument(0) MinecraftServer server, + @Advice.Argument(3) PrimaryLevelData levelData, + @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem, + @Advice.Argument(12) World.Environment env, + @Advice.Argument(value = 13) ChunkGenerator gen + ) { + if (gen == null || !gen.getClass().getPackageName().startsWith("com.volmit.iris")) + return; + + try { + Object bindings = Class.forName("com.volmit.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris") + .getClass() + .getClassLoader()) + .getDeclaredMethod("get") + .invoke(null); + levelStem = (LevelStem) bindings.getClass() + .getDeclaredMethod("levelStem", RegistryAccess.class, ChunkGenerator.class) + .invoke(bindings, server.registryAccess(), gen); + + levelData.customDimensions = null; + } catch (Throwable e) { + throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e); + } + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 862732b5a..3ffcee083 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,6 +30,7 @@ rootProject.name = "Iris" include(":core", ":core:agent") include( + ":nms:v1_21_R5", ":nms:v1_21_R4", ":nms:v1_21_R3", ":nms:v1_21_R2", From 4e8079e431823459bf07f8396481e9217d15dcad Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 4 Jul 2025 00:01:16 +0200 Subject: [PATCH 107/138] use ticket queue for pregen by default --- core/src/main/java/com/volmit/iris/core/IrisSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index c409c1f9d..30216b29e 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -150,7 +150,7 @@ public class IrisSettings { public boolean useCacheByDefault = true; public boolean useHighPriority = false; public boolean useVirtualThreads = false; - public boolean useTicketQueue = false; + public boolean useTicketQueue = true; public int maxConcurrency = 256; } From 44af23ba2ef6dbf5b1e2989aa9c06ea6f284d78e Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 4 Jul 2025 00:06:55 +0200 Subject: [PATCH 108/138] add check for new dimension types --- .../main/java/com/volmit/iris/core/ServerConfigurator.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java index ed833b323..ca8b242d2 100644 --- a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java +++ b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java @@ -221,6 +221,11 @@ public class ServerConfigurator { } } + if (INMS.get().missingDimensionTypes(dimension.getDimensionTypeKey())) { + Iris.warn("The Dimension Type for " + dimension.getLoadFile() + " is not registered on the server."); + warn = true; + } + if (warn) { Iris.error("The Pack " + key + " is INCAPABLE of generating custom biomes"); Iris.error("If not done automatically, restart your server before generating with this pack!"); From 77b425362457c7d06101585306a46aa922da31df Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 4 Jul 2025 12:34:20 +0200 Subject: [PATCH 109/138] fix sentry safeguard info --- core/src/main/java/com/volmit/iris/Iris.java | 2 +- .../iris/core/safeguard/IrisSafeguard.java | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 9f39f70ad..a47b6c617 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -976,7 +976,7 @@ public class Iris extends VolmitPlugin implements Listener { event.setTag("iris.nms", INMS.get().getClass().getCanonicalName()); var context = IrisContext.get(); if (context != null) event.getContexts().set("engine", context.asContext()); - event.getContexts().set("safeguard", ServerBootSFG.allIncompatibilities); + event.getContexts().set("safeguard", IrisSafeguard.asContext()); return event; }); }); diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java b/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java index 241bdd01f..7a4ca9b5c 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java @@ -2,6 +2,8 @@ package com.volmit.iris.core.safeguard; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; import java.util.concurrent.atomic.AtomicBoolean; @@ -35,5 +37,23 @@ public class IrisSafeguard { return "stable"; } } + + public static KMap asContext() { + KMap m = new KMap<>(); + m.put("diskSpace", !ServerBootSFG.hasEnoughDiskSpace); + m.put("javaVersion", !ServerBootSFG.isCorrectJDK); + m.put("jre", ServerBootSFG.isJRE); + m.put("missingAgent", ServerBootSFG.missingAgent); + m.put("missingDimensionTypes", ServerBootSFG.missingDimensionTypes); + m.put("failedInjection", ServerBootSFG.failedInjection); + m.put("unsupportedVersion", ServerBootSFG.unsuportedversion); + m.put("serverSoftware", !ServerBootSFG.passedserversoftware); + KList incompatiblePlugins = new KList<>(); + ServerBootSFG.incompatibilities.forEach((plugin, present) -> { + if (present) incompatiblePlugins.add(plugin); + }); + m.put("plugins", incompatiblePlugins); + return m; + } } From 54402faea8a25e99bb9dc2cbad139894d387d00a Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 4 Jul 2025 13:18:28 +0200 Subject: [PATCH 110/138] fix automatic deepslate variant replacement --- core/src/main/java/com/volmit/iris/util/data/B.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/util/data/B.java b/core/src/main/java/com/volmit/iris/util/data/B.java index 02e0e89cc..1c7a521cb 100644 --- a/core/src/main/java/com/volmit/iris/util/data/B.java +++ b/core/src/main/java/com/volmit/iris/util/data/B.java @@ -105,7 +105,15 @@ public class B { DEEPSLATE_TILES, DEEPSLATE_TILE_STAIRS, DEEPSLATE_TILE_WALL, - CRACKED_DEEPSLATE_TILES + CRACKED_DEEPSLATE_TILES, + DEEPSLATE_COAL_ORE, + DEEPSLATE_IRON_ORE, + DEEPSLATE_COPPER_ORE, + DEEPSLATE_DIAMOND_ORE, + DEEPSLATE_EMERALD_ORE, + DEEPSLATE_GOLD_ORE, + DEEPSLATE_LAPIS_ORE, + DEEPSLATE_REDSTONE_ORE, }).forEach((i) -> b.add(i.ordinal())); return IntSets.unmodifiable(b); From 86d986dfbcc4e319cd0c85e7cee28ef2ae02d059 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 6 Jul 2025 19:40:14 +0200 Subject: [PATCH 111/138] fix deposits spawning in columns --- .../iris/engine/modifier/IrisDepositModifier.java | 12 ++++++++---- .../iris/engine/object/IrisDepositGenerator.java | 5 +++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/modifier/IrisDepositModifier.java b/core/src/main/java/com/volmit/iris/engine/modifier/IrisDepositModifier.java index 037f77d8d..d998088b2 100644 --- a/core/src/main/java/com/volmit/iris/engine/modifier/IrisDepositModifier.java +++ b/core/src/main/java/com/volmit/iris/engine/modifier/IrisDepositModifier.java @@ -52,16 +52,20 @@ public class IrisDepositModifier extends EngineAssignedModifier { BurstExecutor burst = burst().burst(multicore); long seed = x * 341873128712L + z * 132897987541L; + long mask = 0; for (IrisDepositGenerator k : getDimension().getDeposits()) { - burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(seed), x, z, false, context)); + long finalSeed = seed * ++mask; + burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context)); } for (IrisDepositGenerator k : region.getDeposits()) { - burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(seed), x, z, false, context)); + long finalSeed = seed * ++mask; + burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context)); } for (IrisDepositGenerator k : biome.getDeposits()) { - burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(seed), x, z, false, context)); + long finalSeed = seed * ++mask; + burst.queue(() -> generate(k, terrain, rng.nextParallelRNG(finalSeed), x, z, false, context)); } burst.complete(); } @@ -78,7 +82,7 @@ public class IrisDepositModifier extends EngineAssignedModifier { if (k.getPerClumpSpawnChance() < rng.d()) continue; - IrisObject clump = k.getClump(rng, getData()); + IrisObject clump = k.getClump(getEngine(), rng, getData()); int dim = clump.getW(); int min = dim / 2; diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDepositGenerator.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDepositGenerator.java index 271b54ded..19623ca39 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisDepositGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDepositGenerator.java @@ -20,6 +20,7 @@ package com.volmit.iris.engine.object; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KSet; @@ -87,10 +88,10 @@ public class IrisDepositGenerator { @Desc("Ore varience is how many different objects clumps iris will create") private int varience = 3; - public IrisObject getClump(RNG rng, IrisData rdata) { + public IrisObject getClump(Engine engine, RNG rng, IrisData rdata) { KList objects = this.objects.aquire(() -> { - RNG rngv = rng.nextParallelRNG(3957778); + RNG rngv = new RNG(engine.getSeedManager().getDeposit() + hashCode()); KList objectsf = new KList<>(); for (int i = 0; i < varience; i++) { From c9ed4519a8d94a1722836e0edb915a110fbe8d25 Mon Sep 17 00:00:00 2001 From: Julian Krings <47589149+CrazyDev05@users.noreply.github.com> Date: Sun, 6 Jul 2025 20:00:06 +0200 Subject: [PATCH 112/138] remove multiverse core incompatibility warning (#1212) --- build.gradle.kts | 1 + core/build.gradle.kts | 1 + core/src/main/java/com/volmit/iris/Iris.java | 13 -- .../iris/core/link/MultiverseCoreLink.java | 144 +++++------------- .../iris/core/safeguard/ServerBootSFG.java | 2 - .../volmit/iris/core/tools/IrisCreator.java | 4 +- 6 files changed, 44 insertions(+), 121 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2fc7ed8b5..5681a8cf4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -194,6 +194,7 @@ allprojects { maven("https://hub.jeff-media.com/nexus/repository/jeff-media-public/") maven("https://repo.nexomc.com/releases/") maven("https://nexus.phoenixdevt.fr/repository/maven-public/") + maven("https://repo.onarandombox.com/content/groups/public/") } dependencies { diff --git a/core/build.gradle.kts b/core/build.gradle.kts index e2facc0dc..17158627f 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -59,6 +59,7 @@ dependencies { compileOnly("me.kryniowesegryderiusz:kgenerators-core:7.3") { isTransitive = false } + compileOnly("org.mvplugins.multiverse.core:multiverse-core:5.1.0") //implementation files("libs/CustomItems.jar") // Shaded diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index a47b6c617..7904a6f20 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -38,7 +38,6 @@ import com.volmit.iris.engine.object.IrisCompat; import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisWorld; import com.volmit.iris.engine.platform.BukkitChunkGenerator; -import com.volmit.iris.engine.platform.DummyChunkGenerator; import com.volmit.iris.core.safeguard.IrisSafeguard; import com.volmit.iris.core.safeguard.UtilsSFG; import com.volmit.iris.engine.platform.PlatformChunkGenerator; @@ -756,18 +755,6 @@ public class Iris extends VolmitPlugin implements Listener { @Override public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { Iris.debug("Default World Generator Called for " + worldName + " using ID: " + id); - if (worldName.equals("test")) { - try { - throw new RuntimeException(); - } catch (Throwable e) { - Iris.info(e.getStackTrace()[1].getClassName()); - if (e.getStackTrace()[1].getClassName().contains("com.onarandombox.MultiverseCore")) { - Iris.debug("MVC Test detected, Quick! Send them the dummy!"); - return new DummyChunkGenerator(); - } - } - } - if (id == null || id.isEmpty()) id = IrisSettings.get().getGenerator().getDefaultWorldType(); Iris.debug("Generator ID: " + id + " requested by bukkit/plugin"); IrisDimension dim = loadDimension(worldName, id); diff --git a/core/src/main/java/com/volmit/iris/core/link/MultiverseCoreLink.java b/core/src/main/java/com/volmit/iris/core/link/MultiverseCoreLink.java index c483ce756..920abeb5c 100644 --- a/core/src/main/java/com/volmit/iris/core/link/MultiverseCoreLink.java +++ b/core/src/main/java/com/volmit/iris/core/link/MultiverseCoreLink.java @@ -18,126 +18,60 @@ package com.volmit.iris.core.link; -import com.volmit.iris.Iris; -import com.volmit.iris.engine.object.IrisDimension; -import com.volmit.iris.util.collection.KMap; +import lombok.SneakyThrows; import org.bukkit.Bukkit; import org.bukkit.World; -import org.bukkit.WorldType; -import org.bukkit.plugin.Plugin; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Map; +import org.mvplugins.multiverse.core.MultiverseCoreApi; +import org.mvplugins.multiverse.core.world.MultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.options.ImportWorldOptions; public class MultiverseCoreLink { - private final KMap worldNameTypes = new KMap<>(); + private final boolean active; public MultiverseCoreLink() { - - } - - public boolean addWorld(String worldName, IrisDimension dim, String seed) { - if (!isSupported()) { - return false; - } - - try { - Plugin p = getMultiverse(); - Object mvWorldManager = p.getClass().getDeclaredMethod("getMVWorldManager").invoke(p); - Method m = mvWorldManager.getClass().getDeclaredMethod("addWorld", - - String.class, World.Environment.class, String.class, WorldType.class, Boolean.class, String.class, boolean.class); - boolean b = (boolean) m.invoke(mvWorldManager, worldName, dim.getEnvironment(), seed, WorldType.NORMAL, false, "Iris", false); - saveConfig(); - return b; - } catch (Throwable e) { - Iris.reportError(e); - e.printStackTrace(); - } - - return false; - } - - @SuppressWarnings("unchecked") - public Map getList() { - try { - Plugin p = getMultiverse(); - Object mvWorldManager = p.getClass().getDeclaredMethod("getMVWorldManager").invoke(p); - Field f = mvWorldManager.getClass().getDeclaredField("worldsFromTheConfig"); - f.setAccessible(true); - return (Map) f.get(mvWorldManager); - } catch (Throwable e) { - Iris.reportError(e); - e.printStackTrace(); - } - - return null; + active = Bukkit.getPluginManager().getPlugin("Multiverse-Core") != null; } public void removeFromConfig(World world) { - if (!isSupported()) { - return; - } - - getList().remove(world.getName()); - saveConfig(); + removeFromConfig(world.getName()); } public void removeFromConfig(String world) { - if (!isSupported()) { - return; + if (!active) return; + var manager = worldManager(); + manager.removeWorld(world).onSuccess(manager::saveWorldsConfig); + } + + @SneakyThrows + public void updateWorld(World bukkitWorld, String pack) { + if (!active) return; + var generator = "Iris:" + pack; + var manager = worldManager(); + var world = manager.getWorld(bukkitWorld).getOrElse(() -> { + var options = ImportWorldOptions.worldName(bukkitWorld.getName()) + .generator(generator) + .environment(bukkitWorld.getEnvironment()) + .useSpawnAdjust(false); + return manager.importWorld(options).get(); + }); + + world.setAutoLoad(false); + if (!generator.equals(world.getGenerator())) { + var field = MultiverseWorld.class.getDeclaredField("worldConfig"); + field.setAccessible(true); + + var config = field.get(world); + config.getClass() + .getDeclaredMethod("setGenerator", String.class) + .invoke(config, generator); } - getList().remove(world); - saveConfig(); + manager.saveWorldsConfig(); } - public void saveConfig() { - try { - Plugin p = getMultiverse(); - Object mvWorldManager = p.getClass().getDeclaredMethod("getMVWorldManager").invoke(p); - mvWorldManager.getClass().getDeclaredMethod("saveWorldsConfig").invoke(mvWorldManager); - } catch (Throwable e) { - Iris.reportError(e); - e.printStackTrace(); - } - } - - public void assignWorldType(String worldName, String type) { - worldNameTypes.put(worldName, type); - } - - public String getWorldNameType(String worldName, String defaultType) { - try { - String t = worldNameTypes.get(worldName); - return t == null ? defaultType : t; - } catch (Throwable e) { - Iris.reportError(e); - return defaultType; - } - } - - public boolean isSupported() { - return getMultiverse() != null; - } - - public Plugin getMultiverse() { - - return Bukkit.getPluginManager().getPlugin("Multiverse-Core"); - } - - public String envName(World.Environment environment) { - if (environment == null) { - return "normal"; - } - - return switch (environment) { - case NORMAL -> "normal"; - case NETHER -> "nether"; - case THE_END -> "end"; - default -> environment.toString().toLowerCase(); - }; - + private WorldManager worldManager() { + var api = MultiverseCoreApi.get(); + return api.getWorldManager(); } } diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java index f3fbbd9dc..8ec35899f 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java @@ -49,9 +49,7 @@ public class ServerBootSFG { Plugin[] plugins = pluginManager.getPlugins(); incompatibilities.clear(); - incompatibilities.put("Multiverse-Core", false); incompatibilities.put("dynmap", false); - incompatibilities.put("TerraformGenerator", false); incompatibilities.put("Stratos", false); String pluginName; diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java b/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java index ddcd5ff85..fac42b051 100644 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java @@ -199,8 +199,10 @@ public class IrisCreator { world.get().setTime(6000); } }); - } else + } else { addToBukkitYml(); + J.s(() -> Iris.linkMultiverseCore.updateWorld(world.get(), dimension)); + } if (pregen != null) { CompletableFuture ff = new CompletableFuture<>(); From cb93e782429952b7cbcd3fbac13a15beac40d036 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 7 Jul 2025 13:18:31 +0200 Subject: [PATCH 113/138] fix safeguard error --- .../com/volmit/iris/core/safeguard/UtilsSFG.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java index b59a24585..064f48db0 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java @@ -10,7 +10,9 @@ public class UtilsSFG { } public static void printIncompatibleWarnings() { - // String SupportedIrisVersion = getDescription().getVersion(); //todo Automatic version + String[] parts = Iris.instance.getDescription().getVersion().split("-"); + String minVersion = parts[1]; + String maxVersion = parts[2]; if (ServerBootSFG.safeguardPassed) { Iris.safeguard(C.BLUE + "0 Conflicts found"); @@ -22,23 +24,18 @@ public class UtilsSFG { Iris.safeguard(C.YELLOW + "" + ServerBootSFG.count + " Conflicts found"); } - if (ServerBootSFG.incompatibilities.get("Multiverse-Core")) { - Iris.safeguard(C.RED + "Multiverse"); - Iris.safeguard(C.RED + "- The plugin Multiverse is not compatible with the server."); - Iris.safeguard(C.RED + "- If you want to have a world manager, consider using PhantomWorlds or MyWorlds instead."); - } if (ServerBootSFG.incompatibilities.get("dynmap")) { Iris.safeguard(C.RED + "Dynmap"); Iris.safeguard(C.RED + "- The plugin Dynmap is not compatible with the server."); Iris.safeguard(C.RED + "- If you want to have a map plugin like Dynmap, consider Bluemap."); } - if (ServerBootSFG.incompatibilities.get("TerraformGenerator") || ServerBootSFG.incompatibilities.get("Stratos")) { - Iris.safeguard(C.YELLOW + "Terraform Generator / Stratos"); + if (ServerBootSFG.incompatibilities.get("Stratos")) { + Iris.safeguard(C.YELLOW + "Stratos"); Iris.safeguard(C.YELLOW + "- Iris is not compatible with other worldgen plugins."); } if (ServerBootSFG.unsuportedversion) { Iris.safeguard(C.RED + "Server Version"); - Iris.safeguard(C.RED + "- Iris only supports 1.20.1 > 1.21.4"); + Iris.safeguard(C.RED + "- Iris only supports " + minVersion + " > " + maxVersion); } if (ServerBootSFG.missingDimensionTypes) { Iris.safeguard(C.RED + "Dimension Types"); From 20c7891c2fbc73031f60cf8d58d6fad009f75e2a Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 7 Jul 2025 22:54:35 +0200 Subject: [PATCH 114/138] add api generation task --- build.gradle.kts | 1 + buildSrc/build.gradle.kts | 11 +++ buildSrc/src/main/kotlin/ApiGenerator.kt | 101 +++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/ApiGenerator.kt diff --git a/build.gradle.kts b/build.gradle.kts index 5681a8cf4..733f6134e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,6 +35,7 @@ plugins { id("xyz.jpenilla.run-paper") version "2.3.1" id("io.sentry.jvm.gradle") version "5.7.0" } +apply() version = "3.6.11-1.20.1-1.21.5" diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 000000000..c94238170 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + kotlin("jvm") version "2.0.20" +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("org.ow2.asm:asm:9.8") +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/ApiGenerator.kt b/buildSrc/src/main/kotlin/ApiGenerator.kt new file mode 100644 index 000000000..f1bd334d5 --- /dev/null +++ b/buildSrc/src/main/kotlin/ApiGenerator.kt @@ -0,0 +1,101 @@ +import org.gradle.api.DefaultTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import org.gradle.jvm.tasks.Jar +import org.objectweb.asm.* +import java.io.File +import java.util.jar.JarFile +import java.util.jar.JarOutputStream + +class ApiGenerator : Plugin { + override fun apply(target: Project) { + target.tasks.register("generateApi", GenerateApiTask::class.java) + } +} + +abstract class GenerateApiTask : DefaultTask() { + init { + group = "iris" + dependsOn("jar") + } + + @InputFile + val inputFile: Provider = project.tasks + .named("jar", Jar::class.java) + .flatMap { it.archiveFile } + .map { it.asFile } + + @OutputFile + val outputFile: Provider = inputFile.map { targetDirectory().resolve(it.name) } + + @TaskAction + fun generate() { + JarFile(inputFile.get()).use { jar -> + JarOutputStream(outputFile.get().outputStream()).use { out -> + jar.stream() + .parallel() + .filter { !it.isDirectory } + .filter { it.name.endsWith(".class") } + .forEach { + val bytes = jar.getInputStream(it).use { input -> + val writer = ClassWriter(ClassWriter.COMPUTE_MAXS) + val visitor = MethodClearingVisitor(writer) + ClassReader(input).accept(visitor, 0) + writer.toByteArray() + } + + synchronized(out) { + out.putNextEntry(it) + out.write(bytes) + out.closeEntry() + } + } + } + } + } + + fun targetDirectory(): File { + val dir = System.getenv("DEPLOY_DIR") ?: return project.layout.buildDirectory.dir("api").get().asFile + return File(dir) + } +} + +private class MethodClearingVisitor( + cv: ClassVisitor +) : ClassVisitor(Opcodes.ASM9, cv) { + + override fun visitMethod( + access: Int, + name: String?, + descriptor: String?, + signature: String?, + exceptions: Array? + ) = ExceptionThrowingMethodVisitor(super.visitMethod(access, name, descriptor, signature, exceptions)) +} + +private class ExceptionThrowingMethodVisitor( + mv: MethodVisitor +) : MethodVisitor(Opcodes.ASM9, mv) { + + override fun visitCode() { + if (mv == null) return + mv.visitCode() + + mv.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalStateException") + mv.visitInsn(Opcodes.DUP) + mv.visitLdcInsn("Only API") + mv.visitMethodInsn( + Opcodes.INVOKESPECIAL, + "java/lang/IllegalStateException", + "", "(Ljava/lang/String;)V", false + ) + mv.visitInsn(Opcodes.ATHROW) + + mv.visitMaxs(0, 0) + mv.visitEnd() + } +} \ No newline at end of file From 2ecb5556192e4b2cf116a4632cd9de329535d2ec Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 7 Jul 2025 23:37:37 +0200 Subject: [PATCH 115/138] add publishing for the api artifact --- build.gradle.kts | 4 ++- buildSrc/src/main/kotlin/ApiGenerator.kt | 35 ++++++++++++++++++------ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 733f6134e..d3cfd5fee 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,10 +35,12 @@ plugins { id("xyz.jpenilla.run-paper") version "2.3.1" id("io.sentry.jvm.gradle") version "5.7.0" } -apply() +group = "com.volmit" version = "3.6.11-1.20.1-1.21.5" +apply() + // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED // ======================== WINDOWS ============================= registerCustomOutputTask("Cyberpwn", "C://Users/cyberpwn/Documents/development/server/plugins") diff --git a/buildSrc/src/main/kotlin/ApiGenerator.kt b/buildSrc/src/main/kotlin/ApiGenerator.kt index f1bd334d5..3c3a3ba3e 100644 --- a/buildSrc/src/main/kotlin/ApiGenerator.kt +++ b/buildSrc/src/main/kotlin/ApiGenerator.kt @@ -1,7 +1,9 @@ import org.gradle.api.DefaultTask import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.provider.Provider +import org.gradle.api.publish.PublishingExtension +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.api.publish.maven.plugins.MavenPublishPlugin import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction @@ -12,8 +14,17 @@ import java.util.jar.JarFile import java.util.jar.JarOutputStream class ApiGenerator : Plugin { - override fun apply(target: Project) { - target.tasks.register("generateApi", GenerateApiTask::class.java) + override fun apply(target: Project): Unit = with(target) { + plugins.apply(MavenPublishPlugin::class.java) + val task = tasks.register("irisApi", GenerateApiTask::class.java) + extensions.findByType(PublishingExtension::class.java)!! + .publications + .create("maven", MavenPublication::class.java) { + it.groupId = group.toString() + it.artifactId = name + it.version = version.toString() + it.artifact(task) + } } } @@ -21,21 +32,27 @@ abstract class GenerateApiTask : DefaultTask() { init { group = "iris" dependsOn("jar") + finalizedBy("publishToMavenLocal") + doLast { + logger.lifecycle("The API is located at ${outputFile.absolutePath}") + } } @InputFile - val inputFile: Provider = project.tasks + val inputFile: File = project.tasks .named("jar", Jar::class.java) - .flatMap { it.archiveFile } - .map { it.asFile } + .get() + .archiveFile + .get() + .asFile @OutputFile - val outputFile: Provider = inputFile.map { targetDirectory().resolve(it.name) } + val outputFile: File = targetDirectory().resolve(inputFile.name) @TaskAction fun generate() { - JarFile(inputFile.get()).use { jar -> - JarOutputStream(outputFile.get().outputStream()).use { out -> + JarFile(inputFile.apply { mkdirs() }).use { jar -> + JarOutputStream(outputFile.apply { mkdirs() }.outputStream()).use { out -> jar.stream() .parallel() .filter { !it.isDirectory } From 9324b1b5c061e92420b844a7b58a72fd397595d1 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 7 Jul 2025 23:42:30 +0200 Subject: [PATCH 116/138] fix api file generation --- buildSrc/src/main/kotlin/ApiGenerator.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/ApiGenerator.kt b/buildSrc/src/main/kotlin/ApiGenerator.kt index 3c3a3ba3e..a844f0fd2 100644 --- a/buildSrc/src/main/kotlin/ApiGenerator.kt +++ b/buildSrc/src/main/kotlin/ApiGenerator.kt @@ -51,8 +51,8 @@ abstract class GenerateApiTask : DefaultTask() { @TaskAction fun generate() { - JarFile(inputFile.apply { mkdirs() }).use { jar -> - JarOutputStream(outputFile.apply { mkdirs() }.outputStream()).use { out -> + JarFile(inputFile).use { jar -> + JarOutputStream(outputFile.apply { parentFile?.mkdirs() }.outputStream()).use { out -> jar.stream() .parallel() .filter { !it.isDirectory } From 12777bc3f0c343a31520a36ec59eea9e43442c31 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 7 Jul 2025 23:59:42 +0200 Subject: [PATCH 117/138] does this shit work now?? --- build.gradle.kts | 1 + buildSrc/src/main/kotlin/ApiGenerator.kt | 2 -- settings.gradle.kts | 7 ------- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d3cfd5fee..f06c0383d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,6 +30,7 @@ buildscript { plugins { java `java-library` + `maven-publish` id("com.gradleup.shadow") version "8.3.6" id("de.undercouch.download") version "5.0.1" id("xyz.jpenilla.run-paper") version "2.3.1" diff --git a/buildSrc/src/main/kotlin/ApiGenerator.kt b/buildSrc/src/main/kotlin/ApiGenerator.kt index a844f0fd2..b526a1379 100644 --- a/buildSrc/src/main/kotlin/ApiGenerator.kt +++ b/buildSrc/src/main/kotlin/ApiGenerator.kt @@ -3,7 +3,6 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.maven.MavenPublication -import org.gradle.api.publish.maven.plugins.MavenPublishPlugin import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction @@ -15,7 +14,6 @@ import java.util.jar.JarOutputStream class ApiGenerator : Plugin { override fun apply(target: Project): Unit = with(target) { - plugins.apply(MavenPublishPlugin::class.java) val task = tasks.register("irisApi", GenerateApiTask::class.java) extensions.findByType(PublishingExtension::class.java)!! .publications diff --git a/settings.gradle.kts b/settings.gradle.kts index 3ffcee083..18cbee3fe 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,13 +15,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -pluginManagement { - repositories { - mavenLocal() - mavenCentral() - gradlePluginPortal() - } -} plugins { id ("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } From fbc5cee300885ba16cc9bb8c6b4b6a343c178eb4 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 8 Jul 2025 00:22:31 +0200 Subject: [PATCH 118/138] if it still doesn't work, idk what will --- build.gradle.kts | 1 - buildSrc/src/main/kotlin/ApiGenerator.kt | 25 ++++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index f06c0383d..d3cfd5fee 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,7 +30,6 @@ buildscript { plugins { java `java-library` - `maven-publish` id("com.gradleup.shadow") version "8.3.6" id("de.undercouch.download") version "5.0.1" id("xyz.jpenilla.run-paper") version "2.3.1" diff --git a/buildSrc/src/main/kotlin/ApiGenerator.kt b/buildSrc/src/main/kotlin/ApiGenerator.kt index b526a1379..a4138269f 100644 --- a/buildSrc/src/main/kotlin/ApiGenerator.kt +++ b/buildSrc/src/main/kotlin/ApiGenerator.kt @@ -14,15 +14,20 @@ import java.util.jar.JarOutputStream class ApiGenerator : Plugin { override fun apply(target: Project): Unit = with(target) { + plugins.apply("maven-publish") val task = tasks.register("irisApi", GenerateApiTask::class.java) - extensions.findByType(PublishingExtension::class.java)!! - .publications - .create("maven", MavenPublication::class.java) { - it.groupId = group.toString() - it.artifactId = name + extensions.findByType(PublishingExtension::class.java)!!.apply { + repositories.maven { + it.name = "deployDir" + it.url = targetDirectory.toURI() + } + + publications.create("maven", MavenPublication::class.java) { + it.groupId = name it.version = version.toString() it.artifact(task) } + } } } @@ -45,7 +50,7 @@ abstract class GenerateApiTask : DefaultTask() { .asFile @OutputFile - val outputFile: File = targetDirectory().resolve(inputFile.name) + val outputFile: File = project.targetDirectory.resolve(inputFile.name) @TaskAction fun generate() { @@ -72,11 +77,11 @@ abstract class GenerateApiTask : DefaultTask() { } } } +} - fun targetDirectory(): File { - val dir = System.getenv("DEPLOY_DIR") ?: return project.layout.buildDirectory.dir("api").get().asFile - return File(dir) - } +val Project.targetDirectory: File get() { + val dir = System.getenv("DEPLOY_DIR") ?: return project.layout.buildDirectory.dir("api").get().asFile + return File(dir) } private class MethodClearingVisitor( From f9888d19a594261aa07bc35bb2900bd434115ded Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 8 Jul 2025 00:24:03 +0200 Subject: [PATCH 119/138] whoops forgot to update publish task --- buildSrc/src/main/kotlin/ApiGenerator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/ApiGenerator.kt b/buildSrc/src/main/kotlin/ApiGenerator.kt index a4138269f..530d4c6eb 100644 --- a/buildSrc/src/main/kotlin/ApiGenerator.kt +++ b/buildSrc/src/main/kotlin/ApiGenerator.kt @@ -35,7 +35,7 @@ abstract class GenerateApiTask : DefaultTask() { init { group = "iris" dependsOn("jar") - finalizedBy("publishToMavenLocal") + finalizedBy("publishMavenPublicationToDeployDirRepository") doLast { logger.lifecycle("The API is located at ${outputFile.absolutePath}") } From 2e2ea8f1e4388c650de2bf8f87e04ca253f69b5c Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 8 Jul 2025 09:19:57 +0200 Subject: [PATCH 120/138] use paper plugin loader to avoid maven central warning --- core/build.gradle.kts | 3 ++ core/paper-loader/build.gradle.kts | 3 ++ .../com/volmit/iris/IrisPluginLoader.java | 37 +++++++++++++++++++ core/src/main/resources/plugin.yml | 5 ++- settings.gradle.kts | 2 +- 5 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 core/paper-loader/build.gradle.kts create mode 100644 core/paper-loader/src/main/java/com/volmit/iris/IrisPluginLoader.java diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 17158627f..4d7f24b67 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -25,6 +25,7 @@ plugins { val apiVersion = "1.19" val main = "com.volmit.iris.Iris" +val loader = "com.volmit.iris.IrisPluginLoader" val dynamic: Configuration by configurations.creating configurations.compileOnly { extendsFrom(dynamic) } @@ -69,6 +70,7 @@ dependencies { implementation("net.kyori:adventure-platform-bukkit:4.3.4") implementation("net.kyori:adventure-api:4.17.0") implementation("org.bstats:bstats-bukkit:3.1.0") + implementation(project(":core:paper-loader")) // Dynamically Loaded dynamic("commons-io:commons-io:2.13.0") @@ -118,6 +120,7 @@ tasks { "version" to rootProject.version, "apiVersion" to apiVersion, "main" to main, + "loader" to loader, "libraries" to dynamic.allDependencies.map { "\n - $it" }.sorted().joinToString("") ) filesMatching("**/plugin.yml") { diff --git a/core/paper-loader/build.gradle.kts b/core/paper-loader/build.gradle.kts new file mode 100644 index 000000000..367c9bbc9 --- /dev/null +++ b/core/paper-loader/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + compileOnly("io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT") +} \ No newline at end of file diff --git a/core/paper-loader/src/main/java/com/volmit/iris/IrisPluginLoader.java b/core/paper-loader/src/main/java/com/volmit/iris/IrisPluginLoader.java new file mode 100644 index 000000000..c032b5b22 --- /dev/null +++ b/core/paper-loader/src/main/java/com/volmit/iris/IrisPluginLoader.java @@ -0,0 +1,37 @@ +package com.volmit.iris; + +import io.papermc.paper.plugin.loader.PluginClasspathBuilder; +import io.papermc.paper.plugin.loader.PluginLoader; +import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver; +import lombok.SneakyThrows; +import org.bukkit.configuration.file.YamlConfiguration; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.repository.RemoteRepository; +import org.jetbrains.annotations.NotNull; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URI; + +@SuppressWarnings("all") +public class IrisPluginLoader implements PluginLoader { + + @SneakyThrows + @Override + public void classloader(@NotNull PluginClasspathBuilder builder) { + var pluginUri = URI.create("jar:file:" + builder.getContext().getPluginSource().toAbsolutePath() + "!/plugin.yml"); + var plugin = YamlConfiguration.loadConfiguration(new BufferedReader(new InputStreamReader(pluginUri.toURL().openStream()))); + var repository = "https://maven-central.storage-download.googleapis.com/maven2"; + + try { + var field = MavenLibraryResolver.class.getDeclaredField("MAVEN_CENTRAL_DEFAULT_MIRROR"); + repository = (String) field.get(null); + } catch (Throwable e) {} + + var resolver = new MavenLibraryResolver(); + resolver.addRepository(new RemoteRepository.Builder("central", "default", repository).build()); + plugin.getStringList("libraries").forEach(library -> resolver.addDependency(new Dependency(new DefaultArtifact(library), null))); + builder.addLibrary(resolver); + } +} diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml index 89fbfb449..5b6fc940c 100644 --- a/core/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -10,4 +10,7 @@ commands: iris: aliases: [ ir, irs ] api-version: '${apiVersion}' -hotload-dependencies: false \ No newline at end of file +hotload-dependencies: false + +paper-plugin-loader: ${loader} +paper-skip-libraries: true \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 18cbee3fe..138a3c25c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,7 +21,7 @@ plugins { rootProject.name = "Iris" -include(":core", ":core:agent") +include(":core", ":core:agent", ":core:paper-loader") include( ":nms:v1_21_R5", ":nms:v1_21_R4", From 6b193f695a6d5f7ebb508c4d76484ab3d343d6ea Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 8 Jul 2025 18:46:59 +0200 Subject: [PATCH 121/138] switch to slimjar --- build.gradle.kts | 2 +- core/build.gradle.kts | 82 +++++----- core/paper-loader/build.gradle.kts | 3 - .../com/volmit/iris/IrisPluginLoader.java | 37 ----- core/src/main/java/com/volmit/iris/Iris.java | 128 ++------------- .../iris/core/safeguard/IrisSafeguard.java | 5 + .../com/volmit/iris/util/misc/Bindings.java | 146 ++++++++++++++++++ core/src/main/resources/plugin.yml | 7 +- settings.gradle.kts | 2 +- 9 files changed, 215 insertions(+), 197 deletions(-) delete mode 100644 core/paper-loader/build.gradle.kts delete mode 100644 core/paper-loader/src/main/java/com/volmit/iris/IrisPluginLoader.java create mode 100644 core/src/main/java/com/volmit/iris/util/misc/Bindings.java diff --git a/build.gradle.kts b/build.gradle.kts index d3cfd5fee..44ef145a6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,7 +30,7 @@ buildscript { plugins { java `java-library` - id("com.gradleup.shadow") version "8.3.6" + id("com.gradleup.shadow") version "9.0.0-rc1" id("de.undercouch.download") version "5.0.1" id("xyz.jpenilla.run-paper") version "2.3.1" id("io.sentry.jvm.gradle") version "5.7.0" diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 4d7f24b67..9aa0f57a2 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,3 +1,7 @@ +import io.github.slimjar.func.slimjar +import io.github.slimjar.resolver.data.Mirror +import java.net.URI + /* * Iris is a World Generator for Minecraft Bukkit Servers * Copyright (c) 2021 Arcane Arts (Volmit Software) @@ -21,14 +25,11 @@ plugins { `java-library` id("com.gradleup.shadow") id("io.sentry.jvm.gradle") + id("de.crazydev22.slimjar") version "2.0.6" } val apiVersion = "1.19" val main = "com.volmit.iris.Iris" -val loader = "com.volmit.iris.IrisPluginLoader" - -val dynamic: Configuration by configurations.creating -configurations.compileOnly { extendsFrom(dynamic) } /** * Dependencies. @@ -64,30 +65,32 @@ dependencies { //implementation files("libs/CustomItems.jar") // Shaded - implementation("com.dfsek:paralithic:0.8.1") - implementation("io.papermc:paperlib:1.0.5") - implementation("net.kyori:adventure-text-minimessage:4.17.0") - implementation("net.kyori:adventure-platform-bukkit:4.3.4") - implementation("net.kyori:adventure-api:4.17.0") - implementation("org.bstats:bstats-bukkit:3.1.0") - implementation(project(":core:paper-loader")) + implementation(slimjar()) // Dynamically Loaded - dynamic("commons-io:commons-io:2.13.0") - dynamic("commons-lang:commons-lang:2.6") - dynamic("com.github.oshi:oshi-core:6.6.5") - dynamic("org.lz4:lz4-java:1.8.0") - dynamic("it.unimi.dsi:fastutil:8.5.8") - dynamic("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2") - dynamic("org.zeroturnaround:zt-zip:1.14") - dynamic("com.google.code.gson:gson:2.10.1") - dynamic("org.ow2.asm:asm:9.8") - dynamic("bsf:bsf:2.4.0") - dynamic("rhino:js:1.7R2") - dynamic("com.github.ben-manes.caffeine:caffeine:3.0.6") - dynamic("org.apache.commons:commons-lang3:3.12.0") - dynamic("net.bytebuddy:byte-buddy:1.17.5") - dynamic("net.bytebuddy:byte-buddy-agent:1.17.5") + slim("com.dfsek:paralithic:0.8.1") + slim("io.papermc:paperlib:1.0.5") + slim("net.kyori:adventure-text-minimessage:4.17.0") + slim("net.kyori:adventure-platform-bukkit:4.3.4") + slim("net.kyori:adventure-api:4.17.0") + slim("org.bstats:bstats-bukkit:3.1.0") + slim("io.sentry:sentry:8.12.0") + + slim("commons-io:commons-io:2.13.0") + slim("commons-lang:commons-lang:2.6") + slim("com.github.oshi:oshi-core:6.6.5") + slim("org.lz4:lz4-java:1.8.0") + slim("it.unimi.dsi:fastutil:8.5.8") + slim("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2") + slim("org.zeroturnaround:zt-zip:1.14") + slim("com.google.code.gson:gson:2.10.1") + slim("org.ow2.asm:asm:9.8") + slim("bsf:bsf:2.4.0") + slim("rhino:js:1.7R2") + slim("com.github.ben-manes.caffeine:caffeine:3.0.6") + slim("org.apache.commons:commons-lang3:3.12.0") + slim("net.bytebuddy:byte-buddy:1.17.5") + slim("net.bytebuddy:byte-buddy-agent:1.17.5") } java { @@ -95,6 +98,7 @@ java { } sentry { + autoInstallation.enabled = false includeSourceContext = true org = "volmit-software" @@ -102,6 +106,20 @@ sentry { authToken = findProperty("sentry.auth.token") as String? ?: System.getenv("SENTRY_AUTH_TOKEN") } +slimJar { + mirrors = listOf(Mirror( + URI.create("https://maven-central.storage-download.googleapis.com/maven2").toURL(), + URI.create("https://repo.maven.apache.org/maven2/").toURL() + )) + + val libs = "com.volmit.iris.util" + relocate("com.dfsek.paralithic", "$libs.paralithic") + relocate("io.papermc.lib", "$libs.paper") + relocate("net.kyori", "$libs.kyori") + relocate("org.bstats", "$libs.metrics") + relocate("io.sentry", "$libs.sentry") +} + tasks { /** * We need parameter meta for the decree command system @@ -120,8 +138,6 @@ tasks { "version" to rootProject.version, "apiVersion" to apiVersion, "main" to main, - "loader" to loader, - "libraries" to dynamic.allDependencies.map { "\n - $it" }.sorted().joinToString("") ) filesMatching("**/plugin.yml") { expand(inputs.properties) @@ -130,17 +146,7 @@ tasks { shadowJar { mergeServiceFiles() - relocate("com.dfsek.paralithic", "com.volmit.iris.util.paralithic") - relocate("io.papermc.lib", "com.volmit.iris.util.paper") - relocate("net.kyori", "com.volmit.iris.util.kyori") - relocate("org.bstats", "com.volmit.iris.util.metrics") - relocate("io.sentry", "com.volmit.iris.util.sentry") - //minimize() - dependencies { - exclude(dependency("org.ow2.asm:asm:")) - exclude(dependency("org.jetbrains:")) - } } } diff --git a/core/paper-loader/build.gradle.kts b/core/paper-loader/build.gradle.kts deleted file mode 100644 index 367c9bbc9..000000000 --- a/core/paper-loader/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - compileOnly("io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT") -} \ No newline at end of file diff --git a/core/paper-loader/src/main/java/com/volmit/iris/IrisPluginLoader.java b/core/paper-loader/src/main/java/com/volmit/iris/IrisPluginLoader.java deleted file mode 100644 index c032b5b22..000000000 --- a/core/paper-loader/src/main/java/com/volmit/iris/IrisPluginLoader.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.volmit.iris; - -import io.papermc.paper.plugin.loader.PluginClasspathBuilder; -import io.papermc.paper.plugin.loader.PluginLoader; -import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver; -import lombok.SneakyThrows; -import org.bukkit.configuration.file.YamlConfiguration; -import org.eclipse.aether.artifact.DefaultArtifact; -import org.eclipse.aether.graph.Dependency; -import org.eclipse.aether.repository.RemoteRepository; -import org.jetbrains.annotations.NotNull; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.URI; - -@SuppressWarnings("all") -public class IrisPluginLoader implements PluginLoader { - - @SneakyThrows - @Override - public void classloader(@NotNull PluginClasspathBuilder builder) { - var pluginUri = URI.create("jar:file:" + builder.getContext().getPluginSource().toAbsolutePath() + "!/plugin.yml"); - var plugin = YamlConfiguration.loadConfiguration(new BufferedReader(new InputStreamReader(pluginUri.toURL().openStream()))); - var repository = "https://maven-central.storage-download.googleapis.com/maven2"; - - try { - var field = MavenLibraryResolver.class.getDeclaredField("MAVEN_CENTRAL_DEFAULT_MIRROR"); - repository = (String) field.get(null); - } catch (Throwable e) {} - - var resolver = new MavenLibraryResolver(); - resolver.addRepository(new RemoteRepository.Builder("central", "default", repository).build()); - plugin.getStringList("libraries").forEach(library -> resolver.addDependency(new Dependency(new DefaultArtifact(library), null))); - builder.addLibrary(resolver); - } -} diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 7904a6f20..7b9c53f4c 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -30,7 +30,6 @@ import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.v1X.NMSBinding1X; import com.volmit.iris.core.pregenerator.LazyPregenerator; -import com.volmit.iris.core.safeguard.ServerBootSFG; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.EnginePanic; @@ -39,11 +38,9 @@ import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisWorld; import com.volmit.iris.engine.platform.BukkitChunkGenerator; import com.volmit.iris.core.safeguard.IrisSafeguard; -import com.volmit.iris.core.safeguard.UtilsSFG; import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; -import com.volmit.iris.util.context.IrisContext; import com.volmit.iris.util.exceptions.IrisException; import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; @@ -52,30 +49,19 @@ import com.volmit.iris.util.io.FileWatcher; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.io.InstanceState; import com.volmit.iris.util.io.JarScanner; -import com.volmit.iris.util.json.JSONException; import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.misc.Bindings; import com.volmit.iris.util.misc.getHardware; import com.volmit.iris.util.parallel.MultiBurst; import com.volmit.iris.util.plugin.IrisService; import com.volmit.iris.util.plugin.VolmitPlugin; import com.volmit.iris.util.plugin.VolmitSender; -import com.volmit.iris.util.reflect.ShadeFix; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.Queue; import com.volmit.iris.util.scheduling.ShurikenQueue; -import com.volmit.iris.util.sentry.Attachments; -import com.volmit.iris.util.sentry.IrisLogger; -import com.volmit.iris.util.sentry.ServerID; -import io.papermc.lib.PaperLib; -import io.sentry.Sentry; +import io.github.slimjar.app.builder.ApplicationBuilder; import lombok.NonNull; -import net.kyori.adventure.platform.bukkit.BukkitAudiences; -import net.kyori.adventure.text.serializer.ComponentSerializer; -import org.bstats.bukkit.Metrics; -import org.bstats.charts.DrilldownPie; -import org.bstats.charts.SimplePie; -import org.bstats.charts.SingleLineChart; import org.bukkit.*; import org.bukkit.block.data.BlockData; import org.bukkit.command.Command; @@ -91,17 +77,13 @@ import org.bukkit.plugin.IllegalPluginAccessException; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import oshi.SystemInfo; import java.io.*; import java.lang.annotation.Annotation; -import java.math.RoundingMode; import java.net.URL; -import java.text.NumberFormat; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import static com.volmit.iris.core.safeguard.IrisSafeguard.*; import static com.volmit.iris.core.safeguard.ServerBootSFG.passedserversoftware; @@ -111,7 +93,7 @@ public class Iris extends VolmitPlugin implements Listener { private static final Queue syncJobs = new ShurikenQueue<>(); public static Iris instance; - public static BukkitAudiences audiences; + public static Bindings.Adventure audiences; public static MultiverseCoreLink linkMultiverseCore; public static MythicMobsLink linkMythicMobs; public static IrisCompat compat; @@ -120,7 +102,6 @@ public class Iris extends VolmitPlugin implements Listener { static { try { - fixShading(); InstanceState.updateInstanceId(); } catch (Throwable ignored) { @@ -397,7 +378,7 @@ public class Iris extends VolmitPlugin implements Listener { } public static void reportError(Throwable e) { - Sentry.captureException(e); + Bindings.capture(e); if (IrisSettings.get().getGeneral().isDebug()) { String n = e.getClass().getCanonicalName() + "-" + e.getStackTrace()[0].getClassName() + "-" + e.getStackTrace()[0].getLineNumber(); @@ -455,14 +436,21 @@ public class Iris extends VolmitPlugin implements Listener { EnginePanic.add(s, v); } - private static void fixShading() { - ShadeFix.fix(ComponentSerializer.class); + public Iris() { + ApplicationBuilder.appending("Iris") + .downloadDirectoryPath(getDataFolder("cache", "libraries").toPath()) + .logger((message, args) -> { + if (!message.startsWith("Loaded library ")) return; + getLogger().info(message.formatted(args)); + }) + .build(); } + private void enable() { instance = this; services = new KMap<>(); setupAudience(); - setupSentry(); + Bindings.setupSentry(); initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class) i.getClass(), (IrisService) i)); IO.delete(new File("iris")); compat = IrisCompat.configured(getDataFile("compat.json")); @@ -476,7 +464,7 @@ public class Iris extends VolmitPlugin implements Listener { services.values().forEach(IrisService::onEnable); services.values().forEach(this::registerListener); J.s(() -> { - J.a(() -> PaperLib.suggestPaper(this)); + J.a(IrisSafeguard::suggestPaper); J.a(() -> IO.delete(getTemp())); J.a(LazyPregenerator::loadLazyGenerators, 100); J.a(this::bstats); @@ -556,7 +544,7 @@ public class Iris extends VolmitPlugin implements Listener { private void setupAudience() { try { - audiences = BukkitAudiences.create(this); + audiences = new Bindings.Adventure(this); } catch (Throwable e) { e.printStackTrace(); IrisSettings.get().getGeneral().setUseConsoleCustomColors(false); @@ -689,50 +677,7 @@ public class Iris extends VolmitPlugin implements Listener { private void bstats() { if (IrisSettings.get().getGeneral().isPluginMetrics()) { - J.s(() -> { - var metrics = new Metrics(Iris.instance, 24220); - metrics.addCustomChart(new SingleLineChart("custom_dimensions", () -> Bukkit.getWorlds() - .stream() - .filter(IrisToolbelt::isIrisWorld) - .mapToInt(w -> 1) - .sum())); - - metrics.addCustomChart(new DrilldownPie("used_packs", () -> Bukkit.getWorlds().stream() - .map(IrisToolbelt::access) - .filter(Objects::nonNull) - .map(PlatformChunkGenerator::getEngine) - .collect(Collectors.toMap(engine -> engine.getDimension().getLoadKey(), engine -> { - var hash32 = engine.getHash32().getNow(null); - if (hash32 == null) return Map.of(); - int version = engine.getDimension().getVersion(); - String checksum = Long.toHexString(hash32); - - return Map.of("v" + version + " (" + checksum + ")", 1); - }, (a, b) -> { - Map merged = new HashMap<>(a); - b.forEach((k, v) -> merged.merge(k, v, Integer::sum)); - return merged; - })))); - - - var info = new SystemInfo().getHardware(); - var cpu = info.getProcessor().getProcessorIdentifier(); - var mem = info.getMemory(); - metrics.addCustomChart(new SimplePie("cpu_model", cpu::getName)); - - var nf = NumberFormat.getInstance(Locale.ENGLISH); - nf.setMinimumFractionDigits(0); - nf.setMaximumFractionDigits(2); - nf.setRoundingMode(RoundingMode.HALF_UP); - - metrics.addCustomChart(new DrilldownPie("memory", () -> { - double total = mem.getTotal() * 1E-9; - double alloc = Math.min(total, Runtime.getRuntime().maxMemory() * 1E-9); - return Map.of(nf.format(alloc), Map.of(nf.format(total), 1)); - })); - - postShutdown.add(metrics::shutdown); - }); + Bindings.setupBstats(this); } } @@ -937,43 +882,4 @@ public class Iris extends VolmitPlugin implements Listener { return -1; } } - - private static boolean suppress(Throwable e) { - return (e instanceof IllegalStateException ex && "zip file closed".equals(ex.getMessage())) || e instanceof JSONException; - } - - private static void setupSentry() { - var settings = IrisSettings.get().getSentry(); - if (settings.disableAutoReporting || Sentry.isEnabled() || Boolean.getBoolean("iris.suppressReporting")) return; - Iris.info("Enabling Sentry for anonymous error reporting. You can disable this in the settings."); - Iris.info("Your server ID is: " + ServerID.ID); - Sentry.init(options -> { - options.setDsn("https://b16ecc222e9c1e0c48faecacb906fd89@o4509451052646400.ingest.de.sentry.io/4509452722765904"); - if (settings.debug) { - options.setLogger(new IrisLogger()); - options.setDebug(true); - } - - options.setAttachServerName(false); - options.setEnableUncaughtExceptionHandler(false); - options.setRelease(Iris.instance.getDescription().getVersion()); - options.setBeforeSend((event, hint) -> { - if (suppress(event.getThrowable())) return null; - event.setTag("iris.safeguard", IrisSafeguard.mode()); - event.setTag("iris.nms", INMS.get().getClass().getCanonicalName()); - var context = IrisContext.get(); - if (context != null) event.getContexts().set("engine", context.asContext()); - event.getContexts().set("safeguard", IrisSafeguard.asContext()); - return event; - }); - }); - Sentry.configureScope(scope -> { - if (settings.includeServerId) scope.setUser(ServerID.asUser()); - scope.addAttachment(Attachments.PLUGINS); - scope.setTag("server", Bukkit.getVersion()); - scope.setTag("server.type", Bukkit.getName()); - scope.setTag("server.api", Bukkit.getBukkitVersion()); - }); - Runtime.getRuntime().addShutdownHook(new Thread(Sentry::close)); - } } diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java b/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java index 7a4ca9b5c..bef5f165a 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java @@ -4,6 +4,7 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; +import io.papermc.lib.PaperLib; import java.util.concurrent.atomic.AtomicBoolean; @@ -38,6 +39,10 @@ public class IrisSafeguard { } } + public static void suggestPaper() { + PaperLib.suggestPaper(Iris.instance); + } + public static KMap asContext() { KMap m = new KMap<>(); m.put("diskSpace", !ServerBootSFG.hasEnoughDiskSpace); diff --git a/core/src/main/java/com/volmit/iris/util/misc/Bindings.java b/core/src/main/java/com/volmit/iris/util/misc/Bindings.java new file mode 100644 index 000000000..308d3d235 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/misc/Bindings.java @@ -0,0 +1,146 @@ +package com.volmit.iris.util.misc; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.core.safeguard.IrisSafeguard; +import com.volmit.iris.core.tools.IrisToolbelt; +import com.volmit.iris.engine.platform.PlatformChunkGenerator; +import com.volmit.iris.util.context.IrisContext; +import com.volmit.iris.util.json.JSONException; +import com.volmit.iris.util.reflect.ShadeFix; +import com.volmit.iris.util.scheduling.J; +import com.volmit.iris.util.sentry.Attachments; +import com.volmit.iris.util.sentry.IrisLogger; +import com.volmit.iris.util.sentry.ServerID; +import io.sentry.Sentry; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import org.bstats.bukkit.Metrics; +import org.bstats.charts.DrilldownPie; +import org.bstats.charts.SimplePie; +import org.bstats.charts.SingleLineChart; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import oshi.SystemInfo; + +import java.math.RoundingMode; +import java.text.NumberFormat; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +public class Bindings { + + public static void capture(Throwable throwable) { + Sentry.captureException(throwable); + } + + public static void setupSentry() { + var settings = IrisSettings.get().getSentry(); + if (settings.disableAutoReporting || Sentry.isEnabled() || Boolean.getBoolean("iris.suppressReporting")) return; + Iris.info("Enabling Sentry for anonymous error reporting. You can disable this in the settings."); + Iris.info("Your server ID is: " + ServerID.ID); + Sentry.init(options -> { + options.setDsn("https://b16ecc222e9c1e0c48faecacb906fd89@o4509451052646400.ingest.de.sentry.io/4509452722765904"); + if (settings.debug) { + options.setLogger(new IrisLogger()); + options.setDebug(true); + } + + options.setAttachServerName(false); + options.setEnableUncaughtExceptionHandler(false); + options.setRelease(Iris.instance.getDescription().getVersion()); + options.setBeforeSend((event, hint) -> { + if (suppress(event.getThrowable())) return null; + event.setTag("iris.safeguard", IrisSafeguard.mode()); + event.setTag("iris.nms", INMS.get().getClass().getCanonicalName()); + var context = IrisContext.get(); + if (context != null) event.getContexts().set("engine", context.asContext()); + event.getContexts().set("safeguard", IrisSafeguard.asContext()); + return event; + }); + }); + Sentry.configureScope(scope -> { + if (settings.includeServerId) scope.setUser(ServerID.asUser()); + scope.addAttachment(Attachments.PLUGINS); + scope.setTag("server", Bukkit.getVersion()); + scope.setTag("server.type", Bukkit.getName()); + scope.setTag("server.api", Bukkit.getBukkitVersion()); + }); + Runtime.getRuntime().addShutdownHook(new Thread(Sentry::close)); + } + + private static boolean suppress(Throwable e) { + return (e instanceof IllegalStateException ex && "zip file closed".equals(ex.getMessage())) || e instanceof JSONException; + } + + + public static void setupBstats(Iris plugin) { + J.s(() -> { + var metrics = new Metrics(plugin, 24220); + metrics.addCustomChart(new SingleLineChart("custom_dimensions", () -> Bukkit.getWorlds() + .stream() + .filter(IrisToolbelt::isIrisWorld) + .mapToInt(w -> 1) + .sum())); + + metrics.addCustomChart(new DrilldownPie("used_packs", () -> Bukkit.getWorlds().stream() + .map(IrisToolbelt::access) + .filter(Objects::nonNull) + .map(PlatformChunkGenerator::getEngine) + .collect(Collectors.toMap(engine -> engine.getDimension().getLoadKey(), engine -> { + var hash32 = engine.getHash32().getNow(null); + if (hash32 == null) return Map.of(); + int version = engine.getDimension().getVersion(); + String checksum = Long.toHexString(hash32); + + return Map.of("v" + version + " (" + checksum + ")", 1); + }, (a, b) -> { + Map merged = new HashMap<>(a); + b.forEach((k, v) -> merged.merge(k, v, Integer::sum)); + return merged; + })))); + + + var info = new SystemInfo().getHardware(); + var cpu = info.getProcessor().getProcessorIdentifier(); + var mem = info.getMemory(); + metrics.addCustomChart(new SimplePie("cpu_model", cpu::getName)); + + var nf = NumberFormat.getInstance(Locale.ENGLISH); + nf.setMinimumFractionDigits(0); + nf.setMaximumFractionDigits(2); + nf.setRoundingMode(RoundingMode.HALF_UP); + + metrics.addCustomChart(new DrilldownPie("memory", () -> { + double total = mem.getTotal() * 1E-9; + double alloc = Math.min(total, Runtime.getRuntime().maxMemory() * 1E-9); + return Map.of(nf.format(alloc), Map.of(nf.format(total), 1)); + })); + + plugin.postShutdown(metrics::shutdown); + }); + } + + public static class Adventure { + private final BukkitAudiences audiences; + + public Adventure(Iris plugin) { + ShadeFix.fix(ComponentSerializer.class); + this.audiences = BukkitAudiences.create(plugin); + } + + public Audience player(Player player) { + return audiences.player(player); + } + + public Audience sender(CommandSender sender) { + return audiences.sender(sender); + } + } +} diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml index 5b6fc940c..99fa326be 100644 --- a/core/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -5,12 +5,7 @@ load: STARTUP authors: [ cyberpwn, NextdoorPsycho, Vatuu ] website: volmit.com description: More than a Dimension! -libraries: ${libraries} commands: iris: aliases: [ ir, irs ] -api-version: '${apiVersion}' -hotload-dependencies: false - -paper-plugin-loader: ${loader} -paper-skip-libraries: true \ No newline at end of file +api-version: '${apiVersion}' \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 138a3c25c..18cbee3fe 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,7 +21,7 @@ plugins { rootProject.name = "Iris" -include(":core", ":core:agent", ":core:paper-loader") +include(":core", ":core:agent") include( ":nms:v1_21_R5", ":nms:v1_21_R4", From 03c5998c02dd4d83c9a567f6f7d0af20e3cf3372 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 8 Jul 2025 19:08:12 +0200 Subject: [PATCH 122/138] generate a dummy region for standalone focus biomes --- .../java/com/volmit/iris/engine/IrisComplex.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/IrisComplex.java b/core/src/main/java/com/volmit/iris/engine/IrisComplex.java index 652b728ca..70422c6a1 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisComplex.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisComplex.java @@ -42,6 +42,7 @@ import org.bukkit.Material; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; +import java.io.File; import java.util.UUID; @Data @@ -110,8 +111,6 @@ public class IrisComplex implements DataProvider { //@builder if (focusRegion != null) { focusRegion.getAllBiomes(this).forEach(this::registerGenerators); - } else if (focusBiome != null) { - registerGenerators(focusBiome); } else { engine.getDimension() .getRegions() @@ -252,7 +251,15 @@ public class IrisComplex implements DataProvider { } } - return null; + String key = UUID.randomUUID().toString(); + IrisRegion region = new IrisRegion(); + region.getLandBiomes().add(focus.getLoadKey()); + region.getSeaBiomes().add(focus.getLoadKey()); + region.getShoreBiomes().add(focus.getLoadKey()); + region.setLoadKey(key); + region.setLoader(data); + region.setLoadFile(new File(data.getDataFolder(), data.getRegionLoader().getFolderName() + "/" + key + ".json")); + return region; } private IrisDecorator decorateFor(IrisBiome b, double x, double z, IrisDecorationPart part) { From 0b1af51227e40a76f185f3bad8e4275426896ec9 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 8 Jul 2025 19:52:09 +0200 Subject: [PATCH 123/138] add option to disable specific mantle components for testing --- .../iris/core/project/SchemaBuilder.java | 20 ++++++++++ .../volmit/iris/engine/IrisEngineMantle.java | 14 ++++++- .../iris/engine/mantle/ComponentFlag.java | 14 +++++++ .../iris/engine/mantle/EngineMantle.java | 6 ++- .../engine/mantle/IrisMantleComponent.java | 1 + .../iris/engine/mantle/MantleComponent.java | 4 ++ .../components/MantleCarvingComponent.java | 2 + .../components/MantleFluidBodyComponent.java | 2 + .../components/MantleJigsawComponent.java | 2 + .../components/MantleObjectComponent.java | 2 + .../iris/engine/object/IrisDimension.java | 6 +++ .../functions/ComponentFlagFunction.java | 37 +++++++++++++++++++ 12 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/engine/mantle/ComponentFlag.java create mode 100644 core/src/main/java/com/volmit/iris/engine/object/annotations/functions/ComponentFlagFunction.java diff --git a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java index 2e133f944..d856d2b8e 100644 --- a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java +++ b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java @@ -466,6 +466,26 @@ public class SchemaBuilder { items.put("$ref", "#/definitions/" + key); prop.put("items", items); description.add(SYMBOL_TYPE__N + " Must be a valid Enchantment Type (use ctrl+space for auto complete!)"); + } else if (k.isAnnotationPresent(RegistryListFunction.class)) { + var functionClass = k.getDeclaredAnnotation(RegistryListFunction.class).value(); + try { + var instance = functionClass.getDeclaredConstructor().newInstance(); + String key = instance.key(); + fancyType = instance.fancyName(); + + if (!definitions.containsKey(key)) { + JSONObject j = new JSONObject(); + j.put("enum", instance.apply(data)); + definitions.put(key, j); + } + + JSONObject items = new JSONObject(); + items.put("$ref", "#/definitions/" + key); + prop.put("items", items); + description.add(SYMBOL_TYPE__N + " Must be a valid " + fancyType + " (use ctrl+space for auto complete!)"); + } catch (Throwable e) { + Iris.error("Could not execute apply method in " + functionClass.getName()); + } } else if (t.type().equals(PotionEffectType.class)) { fancyType = "List of Potion Effect Types"; String key = "enum-potion-effect-type"; diff --git a/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java b/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java index c3b385361..e58324ad7 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java @@ -29,7 +29,9 @@ import com.volmit.iris.engine.mantle.components.MantleJigsawComponent; import com.volmit.iris.engine.mantle.components.MantleObjectComponent; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.mantle.MantleFlag; import lombok.*; import java.io.File; @@ -44,7 +46,7 @@ public class IrisEngineMantle implements EngineMantle { @Getter(AccessLevel.NONE) private final KMap> components; private final AtomicCache, Integer>>> componentsCache = new AtomicCache<>(); - private final AtomicCache radCache = new AtomicCache<>(); + private final AtomicCache> disabledFlags = new AtomicCache<>(); private final MantleObjectComponent object; private final MantleJigsawComponent jigsaw; @@ -101,10 +103,20 @@ public class IrisEngineMantle implements EngineMantle { @Override public void registerComponent(MantleComponent c) { + c.setEnabled(!getDimension().getDisabledComponents().contains(c.getFlag())); components.computeIfAbsent(c.getPriority(), k -> new KList<>()).add(c); componentsCache.reset(); } + @Override + public KList getComponentFlags() { + return components.values() + .stream() + .flatMap(KList::stream) + .map(MantleComponent::getFlag) + .collect(KList.collector()); + } + @Override public MantleJigsawComponent getJigsawComponent() { return jigsaw; diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/ComponentFlag.java b/core/src/main/java/com/volmit/iris/engine/mantle/ComponentFlag.java new file mode 100644 index 000000000..1c7fe20b2 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/mantle/ComponentFlag.java @@ -0,0 +1,14 @@ +package com.volmit.iris.engine.mantle; + +import com.volmit.iris.util.mantle.MantleFlag; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ComponentFlag { + MantleFlag value(); +} diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java index ba0759c83..d37814e0c 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java @@ -65,6 +65,8 @@ public interface EngineMantle extends IObjectPlacer { void registerComponent(MantleComponent c); + KList getComponentFlags(); + default int getHighest(int x, int z) { return getHighest(x, z, getData()); } @@ -227,7 +229,9 @@ public interface EngineMantle extends IObjectPlacer { } default void generateMantleComponent(MantleWriter writer, int x, int z, MantleComponent c, MantleChunk mc, ChunkContext context) { - mc.raiseFlag(c.getFlag(), () -> c.generateLayer(writer, x, z, context)); + mc.raiseFlag(c.getFlag(), () -> { + if (c.isEnabled()) c.generateLayer(writer, x, z, context); + }); } @ChunkCoordinates diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/IrisMantleComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/IrisMantleComponent.java index 388518c0d..cfe72ae39 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/IrisMantleComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/IrisMantleComponent.java @@ -30,4 +30,5 @@ public abstract class IrisMantleComponent implements MantleComponent { private final EngineMantle engineMantle; private final MantleFlag flag; private final int priority; + private boolean enabled = true; } diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/MantleComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/MantleComponent.java index 3c9c78dc5..64c69d5c6 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/MantleComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/MantleComponent.java @@ -61,6 +61,10 @@ public interface MantleComponent extends Comparable { MantleFlag getFlag(); + boolean isEnabled(); + + void setEnabled(boolean b); + @ChunkCoordinates void generateLayer(MantleWriter writer, int x, int z, ChunkContext context); diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleCarvingComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleCarvingComponent.java index 8d067abfd..eca862c79 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleCarvingComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleCarvingComponent.java @@ -19,6 +19,7 @@ package com.volmit.iris.engine.mantle.components; import com.volmit.iris.engine.data.cache.Cache; +import com.volmit.iris.engine.mantle.ComponentFlag; import com.volmit.iris.engine.mantle.EngineMantle; import com.volmit.iris.engine.mantle.IrisMantleComponent; import com.volmit.iris.engine.mantle.MantleWriter; @@ -32,6 +33,7 @@ import com.volmit.iris.util.math.RNG; import lombok.Getter; @Getter +@ComponentFlag(MantleFlag.CARVED) public class MantleCarvingComponent extends IrisMantleComponent { private final int radius = computeRadius(); diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleFluidBodyComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleFluidBodyComponent.java index bd45353c2..15389715e 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleFluidBodyComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleFluidBodyComponent.java @@ -19,6 +19,7 @@ package com.volmit.iris.engine.mantle.components; import com.volmit.iris.engine.data.cache.Cache; +import com.volmit.iris.engine.mantle.ComponentFlag; import com.volmit.iris.engine.mantle.EngineMantle; import com.volmit.iris.engine.mantle.IrisMantleComponent; import com.volmit.iris.engine.mantle.MantleWriter; @@ -32,6 +33,7 @@ import com.volmit.iris.util.math.RNG; import lombok.Getter; @Getter +@ComponentFlag(MantleFlag.FLUID_BODIES) public class MantleFluidBodyComponent extends IrisMantleComponent { private final int radius = computeRadius(); diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java index 6ba27c542..e5f551872 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java @@ -19,6 +19,7 @@ package com.volmit.iris.engine.mantle.components; import com.volmit.iris.engine.jigsaw.PlannedStructure; +import com.volmit.iris.engine.mantle.ComponentFlag; import com.volmit.iris.engine.mantle.EngineMantle; import com.volmit.iris.engine.mantle.IrisMantleComponent; import com.volmit.iris.engine.mantle.MantleWriter; @@ -39,6 +40,7 @@ import org.jetbrains.annotations.Nullable; import java.util.List; +@ComponentFlag(MantleFlag.JIGSAW) public class MantleJigsawComponent extends IrisMantleComponent { @Getter private final int radius = computeRadius(); diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleObjectComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleObjectComponent.java index b8d4f9fd8..a4ca6dd7a 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleObjectComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleObjectComponent.java @@ -20,6 +20,7 @@ package com.volmit.iris.engine.mantle.components; import com.volmit.iris.Iris; import com.volmit.iris.engine.data.cache.Cache; +import com.volmit.iris.engine.mantle.ComponentFlag; import com.volmit.iris.engine.mantle.EngineMantle; import com.volmit.iris.engine.mantle.IrisMantleComponent; import com.volmit.iris.engine.mantle.MantleWriter; @@ -47,6 +48,7 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; @Getter +@ComponentFlag(MantleFlag.OBJECT) public class MantleObjectComponent extends IrisMantleComponent { private final int radius = computeRadius(); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java index 6ff0fa44f..ef76eac9f 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java @@ -28,11 +28,13 @@ import com.volmit.iris.core.nms.datapack.IDataFixer; import com.volmit.iris.core.nms.datapack.IDataFixer.Dimension; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.object.annotations.*; +import com.volmit.iris.engine.object.annotations.functions.ComponentFlagFunction; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.data.DataProvider; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.mantle.MantleFlag; import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.noise.CNG; @@ -238,6 +240,10 @@ public class IrisDimension extends IrisRegistrant { @MaxNumber(318) @Desc("The Subterrain Fluid Layer Height") private int caveLavaHeight = 8; + @RegistryListFunction(ComponentFlagFunction.class) + @ArrayType(type = MantleFlag.class) + @Desc("Collection of disabled components") + private KList disabledComponents = new KList<>(); public int getMaxHeight() { return (int) getDimensionHeight().getMax(); diff --git a/core/src/main/java/com/volmit/iris/engine/object/annotations/functions/ComponentFlagFunction.java b/core/src/main/java/com/volmit/iris/engine/object/annotations/functions/ComponentFlagFunction.java new file mode 100644 index 000000000..7a0c01755 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/object/annotations/functions/ComponentFlagFunction.java @@ -0,0 +1,37 @@ +package com.volmit.iris.engine.object.annotations.functions; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.engine.framework.ListFunction; +import com.volmit.iris.engine.mantle.ComponentFlag; +import com.volmit.iris.engine.mantle.MantleComponent; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.mantle.MantleFlag; + +import java.util.Objects; + +public class ComponentFlagFunction implements ListFunction> { + @Override + public String key() { + return "component-flag"; + } + + @Override + public String fancyName() { + return "Component Flag"; + } + + @Override + public KList apply(IrisData data) { + var engine = data.getEngine(); + if (engine != null) return engine.getMantle().getComponentFlags().toStringList(); + return Iris.getClasses("com.volmit.iris.engine.mantle.components", ComponentFlag.class) + .stream() + .filter(MantleComponent.class::isAssignableFrom) + .map(c -> c.getDeclaredAnnotation(ComponentFlag.class)) + .filter(Objects::nonNull) + .map(ComponentFlag::value) + .map(MantleFlag::toString) + .collect(KList.collector()); + } +} From a0543bbbf203545d01cbeaab246a90ff7e9dde72 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 8 Jul 2025 19:59:45 +0200 Subject: [PATCH 124/138] relocate slimjar --- core/build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 9aa0f57a2..be3b51086 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -30,6 +30,7 @@ plugins { val apiVersion = "1.19" val main = "com.volmit.iris.Iris" +val libs = "com.volmit.iris.util" /** * Dependencies. @@ -112,7 +113,6 @@ slimJar { URI.create("https://repo.maven.apache.org/maven2/").toURL() )) - val libs = "com.volmit.iris.util" relocate("com.dfsek.paralithic", "$libs.paralithic") relocate("io.papermc.lib", "$libs.paper") relocate("net.kyori", "$libs.kyori") @@ -147,6 +147,7 @@ tasks { shadowJar { mergeServiceFiles() //minimize() + relocate("io.github.slimjar", "$libs.slimjar") } } From 0edaeeec99ea07857d15efe74de0b17d25216a47 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 8 Jul 2025 23:40:10 +0200 Subject: [PATCH 125/138] massive cleanup of maven repos and update dependencies --- build.gradle.kts | 33 ++++++------ core/build.gradle.kts | 91 +++++++++++++++++----------------- gradle/libs.versions.toml | 102 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 64 deletions(-) create mode 100644 gradle/libs.versions.toml diff --git a/build.gradle.kts b/build.gradle.kts index 44ef145a6..fd1f9e400 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,10 +30,10 @@ buildscript { plugins { java `java-library` - id("com.gradleup.shadow") version "9.0.0-rc1" - id("de.undercouch.download") version "5.0.1" - id("xyz.jpenilla.run-paper") version "2.3.1" - id("io.sentry.jvm.gradle") version "5.7.0" + alias(libs.plugins.shadow) + alias(libs.plugins.sentry) + alias(libs.plugins.download) + alias(libs.plugins.runPaper) } group = "com.volmit" @@ -93,8 +93,8 @@ nmsBindings.forEach { key, value -> dependencies { compileOnly(project(":core")) - compileOnly("org.jetbrains:annotations:26.0.2") - compileOnly("net.bytebuddy:byte-buddy:1.17.5") + compileOnly(rootProject.libs.annotations) + compileOnly(rootProject.libs.byteBuddy.core) } } @@ -187,23 +187,20 @@ allprojects { mavenCentral() maven("https://repo.papermc.io/repository/maven-public/") maven("https://repo.codemc.org/repository/maven-public/") - maven("https://mvn.lumine.io/repository/maven-public/") - maven("https://jitpack.io") - maven("https://s01.oss.sonatype.org/content/repositories/snapshots") - maven("https://mvn.lumine.io/repository/maven/") - maven("https://repo.triumphteam.dev/snapshots") - maven("https://repo.mineinabyss.com/releases") - maven("https://hub.jeff-media.com/nexus/repository/jeff-media-public/") - maven("https://repo.nexomc.com/releases/") - maven("https://nexus.phoenixdevt.fr/repository/maven-public/") - maven("https://repo.onarandombox.com/content/groups/public/") + maven("https://jitpack.io") // EcoItems, score + maven("https://repo.nexomc.com/releases/") // nexo + maven("https://maven.devs.beer/") // itemsadder + maven("https://repo.extendedclip.com/releases/") // placeholderapi + maven("https://mvn.lumine.io/repository/maven-public/") // mythic + maven("https://nexus.phoenixdevt.fr/repository/maven-public/") //MMOItems + maven("https://repo.onarandombox.com/content/groups/public/") //Multiverse Core } dependencies { // Provided or Classpath - compileOnly("org.projectlombok:lombok:1.18.36") - annotationProcessor("org.projectlombok:lombok:1.18.36") + compileOnly(rootProject.libs.lombok) + annotationProcessor(rootProject.libs.lombok) } /** diff --git a/core/build.gradle.kts b/core/build.gradle.kts index be3b51086..ef7cc7168 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -23,14 +23,14 @@ import java.net.URI plugins { java `java-library` - id("com.gradleup.shadow") - id("io.sentry.jvm.gradle") - id("de.crazydev22.slimjar") version "2.0.6" + alias(libs.plugins.shadow) + alias(libs.plugins.sentry) + alias(libs.plugins.slimjar) } val apiVersion = "1.19" val main = "com.volmit.iris.Iris" -val libs = "com.volmit.iris.util" +val lib = "com.volmit.iris.util" /** * Dependencies. @@ -46,52 +46,51 @@ val libs = "com.volmit.iris.util" */ dependencies { // Provided or Classpath - compileOnly("org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT") - compileOnly("org.apache.logging.log4j:log4j-api:2.19.0") - compileOnly("org.apache.logging.log4j:log4j-core:2.19.0") + compileOnly(libs.spigot) + compileOnly(libs.log4j.api) + compileOnly(libs.log4j.core) // Third Party Integrations - compileOnly("com.nexomc:nexo:1.6.0") - compileOnly("com.github.LoneDev6:api-itemsadder:3.4.1-r4") - compileOnly("com.github.PlaceholderAPI:placeholderapi:2.11.3") - compileOnly("com.github.Ssomar-Developement:SCore:4.23.10.8") - compileOnly("net.Indyuce:MMOItems-API:6.9.5-SNAPSHOT") - compileOnly("com.willfp:EcoItems:5.44.0") - compileOnly("io.lumine:Mythic-Dist:5.2.1") - compileOnly("io.lumine:MythicCrucible-Dist:2.0.0") - compileOnly("me.kryniowesegryderiusz:kgenerators-core:7.3") { + compileOnly(libs.nexo) + compileOnly(libs.itemsadder) + compileOnly(libs.placeholderApi) + compileOnly(libs.score) + compileOnly(libs.mmoitems) + compileOnly(libs.ecoitems) + compileOnly(libs.mythic) + compileOnly(libs.mythicChrucible) + compileOnly(libs.kgenerators) { isTransitive = false } - compileOnly("org.mvplugins.multiverse.core:multiverse-core:5.1.0") - //implementation files("libs/CustomItems.jar") + compileOnly(libs.multiverseCore) // Shaded implementation(slimjar()) // Dynamically Loaded - slim("com.dfsek:paralithic:0.8.1") - slim("io.papermc:paperlib:1.0.5") - slim("net.kyori:adventure-text-minimessage:4.17.0") - slim("net.kyori:adventure-platform-bukkit:4.3.4") - slim("net.kyori:adventure-api:4.17.0") - slim("org.bstats:bstats-bukkit:3.1.0") - slim("io.sentry:sentry:8.12.0") + slim(libs.paralithic) + slim(libs.paperlib) + slim(libs.adventure.api) + slim(libs.adventure.minimessage) + slim(libs.adventure.platform) + slim(libs.bstats) + slim(libs.sentry) - slim("commons-io:commons-io:2.13.0") - slim("commons-lang:commons-lang:2.6") - slim("com.github.oshi:oshi-core:6.6.5") - slim("org.lz4:lz4-java:1.8.0") - slim("it.unimi.dsi:fastutil:8.5.8") - slim("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2") - slim("org.zeroturnaround:zt-zip:1.14") - slim("com.google.code.gson:gson:2.10.1") - slim("org.ow2.asm:asm:9.8") - slim("bsf:bsf:2.4.0") - slim("rhino:js:1.7R2") - slim("com.github.ben-manes.caffeine:caffeine:3.0.6") - slim("org.apache.commons:commons-lang3:3.12.0") - slim("net.bytebuddy:byte-buddy:1.17.5") - slim("net.bytebuddy:byte-buddy-agent:1.17.5") + slim(libs.commons.io) + slim(libs.commons.lang) + slim(libs.commons.lang3) + slim(libs.oshi) + slim(libs.lz4) + slim(libs.fastutil) + slim(libs.lru) + slim(libs.zip) + slim(libs.gson) + slim(libs.asm) + slim(libs.bsf) + slim(libs.rhino) + slim(libs.caffeine) + slim(libs.byteBuddy.core) + slim(libs.byteBuddy.agent) } java { @@ -113,11 +112,11 @@ slimJar { URI.create("https://repo.maven.apache.org/maven2/").toURL() )) - relocate("com.dfsek.paralithic", "$libs.paralithic") - relocate("io.papermc.lib", "$libs.paper") - relocate("net.kyori", "$libs.kyori") - relocate("org.bstats", "$libs.metrics") - relocate("io.sentry", "$libs.sentry") + relocate("com.dfsek.paralithic", "$lib.paralithic") + relocate("io.papermc.lib", "$lib.paper") + relocate("net.kyori", "$lib.kyori") + relocate("org.bstats", "$lib.metrics") + relocate("io.sentry", "$lib.sentry") } tasks { @@ -147,7 +146,7 @@ tasks { shadowJar { mergeServiceFiles() //minimize() - relocate("io.github.slimjar", "$libs.slimjar") + relocate("io.github.slimjar", "$lib.slimjar") } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 000000000..81d36c3ee --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,102 @@ +# Version catalog is a central place for you to declare and version dependencies +# https://docs.gradle.org/current/userguide/platforms.html#sub:version-catalog +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +# Plugins +shadow = "9.0.0-rc1" # https://plugins.gradle.org/plugin/com.gradleup.shadow +slimjar = "2.0.6" # https://plugins.gradle.org/plugin/de.crazydev22.slimjar +download = "5.6.0" # https://plugins.gradle.org/plugin/de.undercouch.download +runPaper = "2.3.1" # https://plugins.gradle.org/plugin/xyz.jpenilla.run-paper +sentryPlugin = "5.8.0" # https://github.com/getsentry/sentry-android-gradle-plugin + +# Core Libraries +lombok = "1.18.38" +spigot = "1.20.1-R0.1-SNAPSHOT" # https://hub.spigotmc.org/nexus/repository/snapshots/org/spigotmc/spigot-api/maven-metadata.xml +log4j = "2.19.0" # https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-api +adventure-api = "4.23.0" # https://github.com/KyoriPowered/adventure +adventure-platform = "4.4.0" # https://github.com/KyoriPowered/adventure-platform + +annotations = "26.0.2" # https://central.sonatype.com/artifact/org.jetbrains/annotations +paralithic = "0.8.1" # https://github.com/PolyhedralDev/Paralithic/ +paperlib = "1.0.8" # https://github.com/PaperMC/PaperLib/ +bstats = "3.1.0" # https://github.com/Bastian/bstats-metrics/tree/master +sentry = "8.14.0" # https://github.com/getsentry/sentry-java +commons-io = "2.19.0" # https://central.sonatype.com/artifact/commons-io/commons-io +commons-lang = "2.6" # https://central.sonatype.com/artifact/commons-lang/commons-lang +commons-lang3 = "3.17.0" # https://central.sonatype.com/artifact/org.apache.commons/commons-lang3 +oshi = "6.8.2" # https://central.sonatype.com/artifact/com.github.oshi/oshi-core +fastutil = "8.5.16" # https://central.sonatype.com/artifact/it.unimi.dsi/fastutil +lz4 = "1.8.0" # https://central.sonatype.com/artifact/org.lz4/lz4-java +lru = "1.4.2" # https://central.sonatype.com/artifact/com.googlecode.concurrentlinkedhashmap/concurrentlinkedhashmap-lru +zip = "1.17" # https://central.sonatype.com/artifact/org.zeroturnaround/zt-zip +gson = "2.13.1" # https://central.sonatype.com/artifact/com.google.code.gson/gson +asm = "9.8" # https://central.sonatype.com/artifact/org.ow2.asm/asm +bsf = "2.4.0" # https://central.sonatype.com/artifact/bsf/bsf +rhino = "1.7R2" # https://central.sonatype.com/artifact/rhino/js +caffeine = "3.2.1" # https://central.sonatype.com/artifact/com.github.ben-manes.caffeine/caffeine +byte-buddy = "1.17.6" # https://central.sonatype.com/artifact/net.bytebuddy/byte-buddy + +# Third Party Integrations +nexo = "1.8.0" # https://repo.nexomc.com/#/releases/com/nexomc/nexo +itemsadder = "4.0.10" # https://github.com/LoneDev6/API-ItemsAdder +placeholderApi = "2.11.6" # https://repo.extendedclip.com/#/releases/me/clip/placeholderapi +score = "5.25.3.9" # https://github.com/Ssomar-Developement/SCore +mmoitems = "6.9.5-SNAPSHOT" # https://nexus.phoenixdevt.fr/repository/maven-public/net/Indyuce/MMOItems-API/maven-metadata.xml +ecoitems = "5.63.1" # https://github.com/Auxilor/EcoItems/tags +mythic = "5.9.5" +mythic-chrucible = "2.1.0" +kgenerators = "7.3" # https://repo.codemc.io/repository/maven-public/me/kryniowesegryderiusz/kgenerators-core/maven-metadata.xml +multiverseCore = "5.1.0" + +[libraries] +# Core Libraries +lombok = { module = "org.projectlombok:lombok", version.ref ="lombok" } +spigot = { module = "org.spigotmc:spigot-api", version.ref = "spigot" } +log4j-api = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4j" } +log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" } +annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" } + +# Dynamically Loaded +adventure-api = { module = "net.kyori:adventure-api", version.ref = "adventure-api" } +adventure-minimessage = { module = "net.kyori:adventure-text-minimessage", version.ref = "adventure-api" } +adventure-platform = { module = "net.kyori:adventure-platform-bukkit", version.ref = "adventure-platform" } + +paralithic = { module = "com.dfsek:paralithic", version.ref = "paralithic" } +paperlib = { module = "io.papermc:paperlib", version.ref = "paperlib" } +bstats = { module = "org.bstats:bstats-bukkit", version.ref = "bstats" } +sentry = { module = "io.sentry:sentry", version.ref = "sentry" } +commons-io = { module = "commons-io:commons-io", version.ref = "commons-io" } +commons-lang = { module = "commons-lang:commons-lang", version.ref = "commons-lang" } +commons-lang3 = { module = "org.apache.commons:commons-lang3", version.ref = "commons-lang3" } +oshi = { module = "com.github.oshi:oshi-core", version.ref = "oshi" } +lz4 = { module = "org.lz4:lz4-java", version.ref = "lz4" } +fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" } +lru = { module = "com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru", version.ref = "lru" } +zip = { module = "org.zeroturnaround:zt-zip", version.ref = "zip" } +gson = { module = "com.google.code.gson:gson", version.ref = "gson" } +asm = { module = "org.ow2.asm:asm", version.ref = "asm" } +bsf = { module = "bsf:bsf", version.ref = "bsf" } +rhino = { module = "rhino:js", version.ref = "rhino" } +caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version.ref = "caffeine" } +byteBuddy-core = { module = "net.bytebuddy:byte-buddy", version.ref = "byte-buddy" } +byteBuddy-agent = { module = "net.bytebuddy:byte-buddy-agent", version.ref = "byte-buddy" } + +# Third Party Integrations +nexo = { module = "com.nexomc:nexo", version.ref = "nexo" } +itemsadder = { module = "dev.lone:api-itemsadder", version.ref = "itemsadder" } +placeholderApi = { module = "me.clip:placeholderapi", version.ref = "placeholderApi" } +score = { module = "com.github.Ssomar-Developement:SCore", version.ref = "score" } +mmoitems = { module = "net.Indyuce:MMOItems-API", version.ref = "mmoitems" } +ecoitems = { module = "com.willfp:EcoItems", version.ref = "ecoitems" } +mythic = { module = "io.lumine:Mythic-Dist", version.ref = "mythic" } +mythicChrucible = { module = "io.lumine:MythicCrucible-Dist", version.ref = "mythic-chrucible" } +kgenerators = { module = "me.kryniowesegryderiusz:kgenerators-core", version.ref = "kgenerators" } +multiverseCore = { module = "org.mvplugins.multiverse.core:multiverse-core", version.ref = "multiverseCore" } + +[plugins] +shadow = { id = "com.gradleup.shadow", version.ref = "shadow" } +slimjar = { id = "de.crazydev22.slimjar", version.ref = "slimjar" } +download = { id = "de.undercouch.download", version.ref = "download" } +runPaper = { id = "xyz.jpenilla.run-paper", version.ref = "runPaper" } +sentry = { id = "io.sentry.jvm.gradle", version.ref = "sentryPlugin" } From cc49b0f54086f613c9628d6ff842575854525f3b Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 9 Jul 2025 17:37:58 +0200 Subject: [PATCH 126/138] hopefully fix eta --- .../volmit/iris/core/pregenerator/IrisPregenerator.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java b/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java index dbd734a56..e90307dc9 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java @@ -153,12 +153,11 @@ public class IrisPregenerator { } private long computeETA() { - double d = (long) (totalChunks.get() > 1024 ? // Generated chunks exceed 1/8th of total? + double d = (long) (generated.get() > 1024 ? // Generated chunks exceed 1/8th of total? // If yes, use smooth function (which gets more accurate over time since its less sensitive to outliers) - ((totalChunks.get() - generated.get() - cached.get()) * ((double) (M.ms() - startTime.get()) / ((double) generated.get() - cached.get()))) : + ((totalChunks.get() - generated.get()) * ((double) (M.ms() - startTime.get()) / (double) generated.get())) : // If no, use quick function (which is less accurate over time but responds better to the initial delay) - ((totalChunks.get() - generated.get() - cached.get()) / chunksPerSecond.getAverage()) * 1000 - ); + ((totalChunks.get() - generated.get()) / chunksPerSecond.getAverage()) * 1000); return Double.isFinite(d) && d != INVALID ? (long) d : 0; } From 94c578249033e4758b8d1a6e04850d5a8a58850c Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Wed, 9 Jul 2025 18:49:55 +0200 Subject: [PATCH 127/138] move loading message to debug --- core/src/main/java/com/volmit/iris/Iris.java | 13 ++--- .../com/volmit/iris/core/IrisSettings.java | 2 + .../com/volmit/iris/util/misc/SlimJar.java | 58 +++++++++++++++++++ gradle/libs.versions.toml | 2 +- 4 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/util/misc/SlimJar.java diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 7b9c53f4c..be5854cf7 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -52,6 +52,7 @@ import com.volmit.iris.util.io.JarScanner; import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.misc.Bindings; +import com.volmit.iris.util.misc.SlimJar; import com.volmit.iris.util.misc.getHardware; import com.volmit.iris.util.parallel.MultiBurst; import com.volmit.iris.util.plugin.IrisService; @@ -60,7 +61,6 @@ import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.Queue; import com.volmit.iris.util.scheduling.ShurikenQueue; -import io.github.slimjar.app.builder.ApplicationBuilder; import lombok.NonNull; import org.bukkit.*; import org.bukkit.block.data.BlockData; @@ -437,17 +437,12 @@ public class Iris extends VolmitPlugin implements Listener { } public Iris() { - ApplicationBuilder.appending("Iris") - .downloadDirectoryPath(getDataFolder("cache", "libraries").toPath()) - .logger((message, args) -> { - if (!message.startsWith("Loaded library ")) return; - getLogger().info(message.formatted(args)); - }) - .build(); + instance = this; + SlimJar.debug(IrisSettings.get().getSentry().isDebug()); + SlimJar.load(getDataFolder("cache", "libraries")); } private void enable() { - instance = this; services = new KMap<>(); setupAudience(); Bindings.setupSentry(); diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 30216b29e..faaecc8f0 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -23,6 +23,7 @@ import com.volmit.iris.Iris; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.json.JSONException; import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.misc.SlimJar; import com.volmit.iris.util.misc.getHardware; import com.volmit.iris.util.plugin.VolmitSender; import lombok.AllArgsConstructor; @@ -86,6 +87,7 @@ public class IrisSettings { Iris.error("Configuration Error in settings.json! " + ee.getClass().getSimpleName() + ": " + ee.getMessage()); } } + SlimJar.debug(settings.general.debug); return settings; } diff --git a/core/src/main/java/com/volmit/iris/util/misc/SlimJar.java b/core/src/main/java/com/volmit/iris/util/misc/SlimJar.java new file mode 100644 index 000000000..fa79b849d --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/misc/SlimJar.java @@ -0,0 +1,58 @@ +package com.volmit.iris.util.misc; + +import io.github.slimjar.app.builder.ApplicationBuilder; +import io.github.slimjar.logging.ProcessLogger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class SlimJar { + private static final Logger LOGGER = Logger.getLogger("Iris"); + private static final ReentrantLock lock = new ReentrantLock(); + private static final AtomicBoolean loaded = new AtomicBoolean(); + + public static void debug(boolean debug) { + LOGGER.setLevel(debug ? Level.FINE : Level.INFO); + } + + public static void load(@Nullable File localRepository) { + if (loaded.get()) return; + lock.lock(); + + try { + if (loaded.getAndSet(true)) return; + if (localRepository == null) { + localRepository = new File(".iris/libraries"); + } + + LOGGER.info("Loading libraries..."); + ApplicationBuilder.appending("Iris") + .downloadDirectoryPath(localRepository.toPath()) + .logger(new ProcessLogger() { + @Override + public void info(@NotNull String message, @Nullable Object... args) { + LOGGER.fine(message.formatted(args)); + } + + @Override + public void error(@NotNull String message, @Nullable Object... args) { + LOGGER.severe(message.formatted(args)); + } + + @Override + public void debug(@NotNull String message, @Nullable Object... args) { + LOGGER.fine(message.formatted(args)); + } + }) + .build(); + LOGGER.info("Libraries loaded successfully!"); + } finally { + lock.unlock(); + } + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 81d36c3ee..bdc075162 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ [versions] # Plugins shadow = "9.0.0-rc1" # https://plugins.gradle.org/plugin/com.gradleup.shadow -slimjar = "2.0.6" # https://plugins.gradle.org/plugin/de.crazydev22.slimjar +slimjar = "2.0.7" # https://plugins.gradle.org/plugin/de.crazydev22.slimjar download = "5.6.0" # https://plugins.gradle.org/plugin/de.undercouch.download runPaper = "2.3.1" # https://plugins.gradle.org/plugin/xyz.jpenilla.run-paper sentryPlugin = "5.8.0" # https://github.com/getsentry/sentry-android-gradle-plugin From 343dc429d5159b7b840736bc1b636ee3398b698e Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Thu, 10 Jul 2025 19:20:46 +0200 Subject: [PATCH 128/138] fix null pointer when failing to load a dimension --- .../src/main/java/com/volmit/iris/core/ServerConfigurator.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java index ca8b242d2..d249bfbcd 100644 --- a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java +++ b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java @@ -44,6 +44,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicIntegerArray; import java.util.stream.Stream; @@ -140,6 +141,7 @@ public class ServerConfigurator { var loader = data.getDimensionLoader(); return loader.loadAll(loader.getPossibleKeys()) .stream() + .filter(Objects::nonNull) .map(ServerConfigurator::verifyDataPackInstalled) .toList() .contains(false); @@ -280,6 +282,7 @@ public class ServerConfigurator { var loader = data.getDimensionLoader(); return loader.loadAll(loader.getPossibleKeys()) .stream() + .filter(Objects::nonNull) .peek(this::merge); } From 64bb81626c3ce146021089e27ef713ca53b7b5e4 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 11 Jul 2025 12:54:16 +0200 Subject: [PATCH 129/138] update overworld pack --- build.gradle.kts | 2 +- core/src/main/java/com/volmit/iris/core/nms/INMS.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index fd1f9e400..4def2b348 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,7 +37,7 @@ plugins { } group = "com.volmit" -version = "3.6.11-1.20.1-1.21.5" +version = "3.6.11-1.20.1-1.21.7" apply() diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMS.java b/core/src/main/java/com/volmit/iris/core/nms/INMS.java index 848b08879..8b998147c 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMS.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMS.java @@ -40,6 +40,7 @@ public class INMS { "1.21.7", "v1_21_R5" ); private static final List PACKS = List.of( + new Version(21, 5, "31100"), new Version(21, 4, "31020"), new Version(21, 2, "31000"), new Version(20, 1, "3910") From f6590c26e70e6e259ee3ad51993209b7a7bedf90 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 11 Jul 2025 13:07:20 +0200 Subject: [PATCH 130/138] fix download failing for dimensions with snippets --- .../volmit/iris/core/service/StudioSVC.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/service/StudioSVC.java b/core/src/main/java/com/volmit/iris/core/service/StudioSVC.java index 118a11e13..bf070d063 100644 --- a/core/src/main/java/com/volmit/iris/core/service/StudioSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/StudioSVC.java @@ -18,7 +18,6 @@ package com.volmit.iris.core.service; -import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; @@ -250,30 +249,25 @@ public class StudioSVC implements IrisService { return; } - File dimensions = new File(dir, "dimensions"); + IrisData data = IrisData.get(dir); + String[] dimensions = data.getDimensionLoader().getPossibleKeys(); - if (!(dimensions.exists() && dimensions.isDirectory())) { - sender.sendMessage("Invalid Format. Missing dimensions folder"); - return; - } - - if (dimensions.listFiles() == null) { + if (dimensions == null || dimensions.length == 0) { sender.sendMessage("No dimension file found in the extracted zip file."); sender.sendMessage("Check it is there on GitHub and report this to staff!"); - } else if (dimensions.listFiles().length != 1) { + } else if (dimensions.length != 1) { sender.sendMessage("Dimensions folder must have 1 file in it"); return; } - File dim = dimensions.listFiles()[0]; + IrisDimension d = data.getDimensionLoader().load(dimensions[0]); - if (!dim.isFile()) { + if (d == null) { sender.sendMessage("Invalid dimension (folder) in dimensions folder"); return; } - String key = dim.getName().split("\\Q.\\E")[0]; - IrisDimension d = new Gson().fromJson(IO.readAll(dim), IrisDimension.class); + String key = d.getLoadKey(); sender.sendMessage("Importing " + d.getName() + " (" + key + ")"); File packEntry = new File(packs, key); From a9891e819abce21747047cde2b3469442d24f6c5 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 12 Jul 2025 12:51:04 +0200 Subject: [PATCH 131/138] v+ --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4def2b348..462d697a2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,7 +37,7 @@ plugins { } group = "com.volmit" -version = "3.6.11-1.20.1-1.21.7" +version = "3.7.0-1.20.1-1.21.7" apply() From 9ade90d9cafe36ab41653cf4ebc6d14520bfd901 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 12 Jul 2025 15:51:49 +0200 Subject: [PATCH 132/138] make the mythic mobs link a data provider and make adding custom mob plugins easier --- core/src/main/java/com/volmit/iris/Iris.java | 3 - .../iris/core/link/ExternalDataProvider.java | 59 ++++++- .../volmit/iris/core/link/data/DataType.java | 33 ++++ .../link/{ => data}/EcoItemsDataProvider.java | 44 ++--- .../ExecutableItemsDataProvider.java | 45 ++---- .../{ => data}/HMCLeavesDataProvider.java | 48 ++---- .../{ => data}/ItemAdderDataProvider.java | 152 +++++++++--------- .../{ => data}/KGeneratorsDataProvider.java | 36 ++--- .../link/{ => data}/MMOItemsDataProvider.java | 70 ++++---- .../MythicCrucibleDataProvider.java | 76 ++------- .../MythicMobsDataProvider.java} | 91 +++++------ .../link/{ => data}/NexoDataProvider.java | 85 ++++------ .../iris/core/project/SchemaBuilder.java | 16 +- .../iris/core/service/ExternalDataSVC.java | 89 +++++----- .../volmit/iris/engine/object/IrisEntity.java | 15 +- .../java/com/volmit/iris/util/data/B.java | 5 +- .../com/volmit/iris/util/scheduling/J.java | 15 ++ 17 files changed, 401 insertions(+), 481 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/core/link/data/DataType.java rename core/src/main/java/com/volmit/iris/core/link/{ => data}/EcoItemsDataProvider.java (57%) rename core/src/main/java/com/volmit/iris/core/link/{ => data}/ExecutableItemsDataProvider.java (50%) rename core/src/main/java/com/volmit/iris/core/link/{ => data}/HMCLeavesDataProvider.java (81%) rename core/src/main/java/com/volmit/iris/core/link/{ => data}/ItemAdderDataProvider.java (59%) rename core/src/main/java/com/volmit/iris/core/link/{ => data}/KGeneratorsDataProvider.java (73%) rename core/src/main/java/com/volmit/iris/core/link/{ => data}/MMOItemsDataProvider.java (64%) rename core/src/main/java/com/volmit/iris/core/link/{ => data}/MythicCrucibleDataProvider.java (64%) rename core/src/main/java/com/volmit/iris/core/link/{MythicMobsLink.java => data/MythicMobsDataProvider.java} (54%) rename core/src/main/java/com/volmit/iris/core/link/{ => data}/NexoDataProvider.java (63%) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index be5854cf7..f299e4409 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -25,7 +25,6 @@ import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.link.IrisPapiExpansion; import com.volmit.iris.core.link.MultiverseCoreLink; -import com.volmit.iris.core.link.MythicMobsLink; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.v1X.NMSBinding1X; @@ -95,7 +94,6 @@ public class Iris extends VolmitPlugin implements Listener { public static Iris instance; public static Bindings.Adventure audiences; public static MultiverseCoreLink linkMultiverseCore; - public static MythicMobsLink linkMythicMobs; public static IrisCompat compat; public static FileWatcher configWatcher; private static VolmitSender sender; @@ -454,7 +452,6 @@ public class Iris extends VolmitPlugin implements Listener { getSender().setTag(getTag()); IrisSafeguard.splash(true); linkMultiverseCore = new MultiverseCoreLink(); - linkMythicMobs = new MythicMobsLink(); configWatcher = new FileWatcher(getDataFile("settings.json")); services.values().forEach(IrisService::onEnable); services.values().forEach(this::registerListener); diff --git a/core/src/main/java/com/volmit/iris/core/link/ExternalDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/ExternalDataProvider.java index 02bf058a8..bbc7e8088 100644 --- a/core/src/main/java/com/volmit/iris/core/link/ExternalDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/ExternalDataProvider.java @@ -1,24 +1,33 @@ package com.volmit.iris.core.link; +import com.volmit.iris.core.link.data.DataType; +import com.volmit.iris.core.nms.container.Pair; +import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.data.IrisCustomData; +import com.volmit.iris.util.math.RNG; import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Entity; +import org.bukkit.event.Listener; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.MissingResourceException; @Getter @RequiredArgsConstructor -public abstract class ExternalDataProvider { +public abstract class ExternalDataProvider implements Listener { @NonNull private final String pluginId; @@ -53,7 +62,9 @@ public abstract class ExternalDataProvider { * @throws MissingResourceException when the blockId is invalid */ @NotNull - public abstract BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap state) throws MissingResourceException; + public BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap state) throws MissingResourceException { + throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); + } /** * @see ExternalDataProvider#getItemStack(Identifier) @@ -73,7 +84,9 @@ public abstract class ExternalDataProvider { * @throws MissingResourceException when the itemId is invalid */ @NotNull - public abstract ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap customNbt) throws MissingResourceException; + public ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap customNbt) throws MissingResourceException { + throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key()); + } /** * This method is used for placing blocks that need to use the plugins api @@ -85,9 +98,43 @@ public abstract class ExternalDataProvider { */ public void processUpdate(@NotNull Engine engine, @NotNull Block block, @NotNull Identifier blockId) {} - public abstract @NotNull Identifier[] getBlockTypes(); + /** + * Spawns a mob in the specified location using the given engine and entity identifier. + * + * @param location The location in the world where the mob should spawn. Must not be null. + * @param entityId The identifier of the mob entity to spawn. Must not be null. + * @return The spawned {@link Entity} if successful, or null if the mob could not be spawned. + */ + @Nullable + public Entity spawnMob(@NotNull Location location, @NotNull Identifier entityId) throws MissingResourceException { + throw new MissingResourceException("Failed to find Entity!", entityId.namespace(), entityId.key()); + } - public abstract @NotNull Identifier[] getItemTypes(); + public abstract @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType); - public abstract boolean isValidProvider(@NotNull Identifier id, boolean isItem); + public abstract boolean isValidProvider(@NotNull Identifier id, DataType dataType); + + protected static Pair parseYawAndFace(@NotNull Engine engine, @NotNull Block block, @NotNull KMap<@NotNull String, @NotNull String> state) { + float yaw = 0; + BlockFace face = BlockFace.NORTH; + + long seed = engine.getSeedManager().getSeed() + Cache.key(block.getX(), block.getZ()) + block.getY(); + RNG rng = new RNG(seed); + if ("true".equals(state.get("randomYaw"))) { + yaw = rng.f(0, 360); + } else if (state.containsKey("yaw")) { + yaw = Float.parseFloat(state.get("yaw")); + } + if ("true".equals(state.get("randomFace"))) { + BlockFace[] faces = BlockFace.values(); + face = faces[rng.i(0, faces.length - 1)]; + } else if (state.containsKey("face")) { + face = BlockFace.valueOf(state.get("face").toUpperCase()); + } + if (face == BlockFace.SELF) { + face = BlockFace.NORTH; + } + + return new Pair<>(yaw, face); + } } diff --git a/core/src/main/java/com/volmit/iris/core/link/data/DataType.java b/core/src/main/java/com/volmit/iris/core/link/data/DataType.java new file mode 100644 index 000000000..65edfc2a3 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/link/data/DataType.java @@ -0,0 +1,33 @@ +package com.volmit.iris.core.link.data; + +import com.volmit.iris.core.link.ExternalDataProvider; +import com.volmit.iris.core.link.Identifier; + +import java.util.MissingResourceException; +import java.util.function.BiPredicate; +import java.util.function.Predicate; + +public enum DataType implements BiPredicate { + ITEM, + BLOCK, + ENTITY; + + @Override + public boolean test(ExternalDataProvider dataProvider, Identifier identifier) { + if (!dataProvider.isValidProvider(identifier, this)) return false; + try { + switch (this) { + case ITEM -> dataProvider.getItemStack(identifier); + case BLOCK -> dataProvider.getBlockData(identifier); + case ENTITY -> {} + } + return true; + } catch (MissingResourceException e) { + return false; + } + } + + public Predicate asPredicate(ExternalDataProvider dataProvider) { + return i -> test(dataProvider, i); + } +} diff --git a/core/src/main/java/com/volmit/iris/core/link/EcoItemsDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/EcoItemsDataProvider.java similarity index 57% rename from core/src/main/java/com/volmit/iris/core/link/EcoItemsDataProvider.java rename to core/src/main/java/com/volmit/iris/core/link/data/EcoItemsDataProvider.java index 81eb9669a..05e9cd221 100644 --- a/core/src/main/java/com/volmit/iris/core/link/EcoItemsDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/EcoItemsDataProvider.java @@ -1,16 +1,18 @@ -package com.volmit.iris.core.link; +package com.volmit.iris.core.link.data; import com.volmit.iris.Iris; -import com.volmit.iris.util.collection.KList; +import com.volmit.iris.core.link.ExternalDataProvider; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.reflect.WrappedField; import com.willfp.ecoitems.items.EcoItem; import com.willfp.ecoitems.items.EcoItems; import org.bukkit.NamespacedKey; -import org.bukkit.block.data.BlockData; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.List; import java.util.MissingResourceException; public class EcoItemsDataProvider extends ExternalDataProvider { @@ -34,12 +36,6 @@ public class EcoItemsDataProvider extends ExternalDataProvider { } } - @NotNull - @Override - public BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap state) throws MissingResourceException { - throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); - } - @NotNull @Override public ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap customNbt) throws MissingResourceException { @@ -48,30 +44,18 @@ public class EcoItemsDataProvider extends ExternalDataProvider { return itemStack.get(item).clone(); } - @NotNull @Override - public Identifier[] getBlockTypes() { - return new Identifier[0]; - } - - @NotNull - @Override - public Identifier[] getItemTypes() { - KList names = new KList<>(); - for (EcoItem item : EcoItems.INSTANCE.values()) { - try { - Identifier key = Identifier.fromNamespacedKey(id.get(item)); - if (getItemStack(key) != null) - names.add(key); - } catch (MissingResourceException ignored) { - } - } - - return names.toArray(new Identifier[0]); + public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) { + if (dataType != DataType.ITEM) return List.of(); + return EcoItems.INSTANCE.values() + .stream() + .map(x -> Identifier.fromNamespacedKey(id.get(x))) + .filter(dataType.asPredicate(this)) + .toList(); } @Override - public boolean isValidProvider(@NotNull Identifier id, boolean isItem) { - return id.namespace().equalsIgnoreCase("ecoitems") && isItem; + public boolean isValidProvider(@NotNull Identifier id, DataType dataType) { + return id.namespace().equalsIgnoreCase("ecoitems") && dataType == DataType.ITEM; } } diff --git a/core/src/main/java/com/volmit/iris/core/link/ExecutableItemsDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/ExecutableItemsDataProvider.java similarity index 50% rename from core/src/main/java/com/volmit/iris/core/link/ExecutableItemsDataProvider.java rename to core/src/main/java/com/volmit/iris/core/link/data/ExecutableItemsDataProvider.java index 80fc25860..88a2dc943 100644 --- a/core/src/main/java/com/volmit/iris/core/link/ExecutableItemsDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/ExecutableItemsDataProvider.java @@ -1,13 +1,15 @@ -package com.volmit.iris.core.link; +package com.volmit.iris.core.link.data; import com.ssomar.score.api.executableitems.ExecutableItemsAPI; import com.volmit.iris.Iris; -import com.volmit.iris.util.collection.KList; +import com.volmit.iris.core.link.ExternalDataProvider; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.util.collection.KMap; -import org.bukkit.block.data.BlockData; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.List; import java.util.MissingResourceException; import java.util.Optional; @@ -21,12 +23,6 @@ public class ExecutableItemsDataProvider extends ExternalDataProvider { Iris.info("Setting up ExecutableItems Link..."); } - @NotNull - @Override - public BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap state) throws MissingResourceException { - throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); - } - @NotNull @Override public ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap customNbt) throws MissingResourceException { @@ -35,30 +31,19 @@ public class ExecutableItemsDataProvider extends ExternalDataProvider { .orElseThrow(() -> new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key())); } - @NotNull @Override - public Identifier[] getBlockTypes() { - return new Identifier[0]; - } - - @NotNull - @Override - public Identifier[] getItemTypes() { - KList names = new KList<>(); - for (String name : ExecutableItemsAPI.getExecutableItemsManager().getExecutableItemIdsList()) { - try { - Identifier key = new Identifier("executable_items", name); - if (getItemStack(key) != null) - names.add(key); - } catch (MissingResourceException ignored) { - } - } - - return names.toArray(new Identifier[0]); + public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) { + if (dataType != DataType.ITEM) return List.of(); + return ExecutableItemsAPI.getExecutableItemsManager() + .getExecutableItemIdsList() + .stream() + .map(name -> new Identifier("executable_items", name)) + .filter(dataType.asPredicate(this)) + .toList(); } @Override - public boolean isValidProvider(@NotNull Identifier key, boolean isItem) { - return key.namespace().equalsIgnoreCase("executable_items") && isItem; + public boolean isValidProvider(@NotNull Identifier key, DataType dataType) { + return key.namespace().equalsIgnoreCase("executable_items") && dataType == DataType.ITEM; } } diff --git a/core/src/main/java/com/volmit/iris/core/link/HMCLeavesDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/HMCLeavesDataProvider.java similarity index 81% rename from core/src/main/java/com/volmit/iris/core/link/HMCLeavesDataProvider.java rename to core/src/main/java/com/volmit/iris/core/link/data/HMCLeavesDataProvider.java index 5f5a358fd..bbf2cd197 100644 --- a/core/src/main/java/com/volmit/iris/core/link/HMCLeavesDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/HMCLeavesDataProvider.java @@ -1,10 +1,11 @@ -package com.volmit.iris.core.link; +package com.volmit.iris.core.link.data; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.link.ExternalDataProvider; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.service.ExternalDataSVC; import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.data.IrisCustomData; import com.volmit.iris.util.reflect.WrappedField; @@ -18,6 +19,8 @@ import org.bukkit.block.data.type.Leaves; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.MissingResourceException; import java.util.function.Supplier; @@ -89,41 +92,20 @@ public class HMCLeavesDataProvider extends ExternalDataProvider { } } - @NotNull @Override - public Identifier[] getBlockTypes() { - KList names = new KList<>(); - for (String name : blockDataMap.keySet()) { - try { - Identifier key = new Identifier("hmcleaves", name); - if (getBlockData(key) != null) - names.add(key); - } catch (MissingResourceException ignored) { - } - } - - return names.toArray(new Identifier[0]); - } - - @NotNull - @Override - public Identifier[] getItemTypes() { - KList names = new KList<>(); - for (String name : itemDataField.keySet()) { - try { - Identifier key = new Identifier("hmcleaves", name); - if (getItemStack(key) != null) - names.add(key); - } catch (MissingResourceException ignored) { - } - } - - return names.toArray(new Identifier[0]); + public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) { + if (dataType == DataType.ENTITY) return List.of(); + return (dataType == DataType.BLOCK ? blockDataMap.keySet() : itemDataField.keySet()) + .stream() + .map(x -> new Identifier("hmcleaves", x)) + .filter(dataType.asPredicate(this)) + .toList(); } @Override - public boolean isValidProvider(@NotNull Identifier id, boolean isItem) { - return (isItem ? itemDataField.keySet() : blockDataMap.keySet()).contains(id.key()); + public boolean isValidProvider(@NotNull Identifier id, DataType dataType) { + if (dataType == DataType.ENTITY) return false; + return (dataType == DataType.ITEM ? itemDataField.keySet() : blockDataMap.keySet()).contains(id.key()); } private Map getMap(C config, String name) { diff --git a/core/src/main/java/com/volmit/iris/core/link/ItemAdderDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/ItemAdderDataProvider.java similarity index 59% rename from core/src/main/java/com/volmit/iris/core/link/ItemAdderDataProvider.java rename to core/src/main/java/com/volmit/iris/core/link/data/ItemAdderDataProvider.java index d7e891da0..c0bf27ef4 100644 --- a/core/src/main/java/com/volmit/iris/core/link/ItemAdderDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/ItemAdderDataProvider.java @@ -1,76 +1,76 @@ -package com.volmit.iris.core.link; - -import com.volmit.iris.Iris; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.collection.KMap; -import dev.lone.itemsadder.api.CustomBlock; -import dev.lone.itemsadder.api.CustomStack; -import org.bukkit.block.data.BlockData; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; - -import java.util.MissingResourceException; - -public class ItemAdderDataProvider extends ExternalDataProvider { - - private KList itemNamespaces, blockNamespaces; - - public ItemAdderDataProvider() { - super("ItemsAdder"); - } - - @Override - public void init() { - this.itemNamespaces = new KList<>(); - this.blockNamespaces = new KList<>(); - - for (Identifier i : getItemTypes()) { - itemNamespaces.addIfMissing(i.namespace()); - } - for (Identifier i : getBlockTypes()) { - blockNamespaces.addIfMissing(i.namespace()); - Iris.info("Found ItemAdder Block: " + i); - } - } - - @NotNull - @Override - public BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap state) throws MissingResourceException { - return CustomBlock.getBaseBlockData(blockId.toString()); - } - - @NotNull - @Override - public ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap customNbt) throws MissingResourceException { - CustomStack stack = CustomStack.getInstance(itemId.toString()); - if (stack == null) { - throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key()); - } - return stack.getItemStack(); - } - - @NotNull - @Override - public Identifier[] getBlockTypes() { - KList keys = new KList<>(); - for (String s : CustomBlock.getNamespacedIdsInRegistry()) { - keys.add(Identifier.fromString(s)); - } - return keys.toArray(new Identifier[0]); - } - - @NotNull - @Override - public Identifier[] getItemTypes() { - KList keys = new KList<>(); - for (String s : CustomStack.getNamespacedIdsInRegistry()) { - keys.add(Identifier.fromString(s)); - } - return keys.toArray(new Identifier[0]); - } - - @Override - public boolean isValidProvider(@NotNull Identifier id, boolean isItem) { - return isItem ? this.itemNamespaces.contains(id.namespace()) : this.blockNamespaces.contains(id.namespace()); - } -} +package com.volmit.iris.core.link.data; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.link.ExternalDataProvider; +import com.volmit.iris.core.link.Identifier; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import dev.lone.itemsadder.api.CustomBlock; +import dev.lone.itemsadder.api.CustomStack; +import org.bukkit.block.data.BlockData; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.List; +import java.util.MissingResourceException; + +public class ItemAdderDataProvider extends ExternalDataProvider { + + private KList itemNamespaces, blockNamespaces; + + public ItemAdderDataProvider() { + super("ItemsAdder"); + } + + @Override + public void init() { + this.itemNamespaces = new KList<>(); + this.blockNamespaces = new KList<>(); + + for (Identifier i : getTypes(DataType.ITEM)) { + itemNamespaces.addIfMissing(i.namespace()); + } + for (Identifier i : getTypes(DataType.BLOCK)) { + blockNamespaces.addIfMissing(i.namespace()); + Iris.info("Found ItemAdder Block: " + i); + } + } + + @NotNull + @Override + public BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap state) throws MissingResourceException { + return CustomBlock.getBaseBlockData(blockId.toString()); + } + + @NotNull + @Override + public ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap customNbt) throws MissingResourceException { + CustomStack stack = CustomStack.getInstance(itemId.toString()); + if (stack == null) { + throw new MissingResourceException("Failed to find ItemData!", itemId.namespace(), itemId.key()); + } + return stack.getItemStack(); + } + + @Override + public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) { + return switch (dataType) { + case ENTITY -> List.of(); + case ITEM -> CustomStack.getNamespacedIdsInRegistry() + .stream() + .map(Identifier::fromString) + .toList(); + case BLOCK -> CustomBlock.getNamespacedIdsInRegistry() + .stream() + .map(Identifier::fromString) + .toList(); + }; + } + + @Override + public boolean isValidProvider(@NotNull Identifier id, DataType dataType) { + if (dataType == DataType.ENTITY) return false; + return dataType == DataType.ITEM ? this.itemNamespaces.contains(id.namespace()) : this.blockNamespaces.contains(id.namespace()); + } +} diff --git a/core/src/main/java/com/volmit/iris/core/link/KGeneratorsDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/KGeneratorsDataProvider.java similarity index 73% rename from core/src/main/java/com/volmit/iris/core/link/KGeneratorsDataProvider.java rename to core/src/main/java/com/volmit/iris/core/link/data/KGeneratorsDataProvider.java index 618fe4ccc..ea4f6e4ef 100644 --- a/core/src/main/java/com/volmit/iris/core/link/KGeneratorsDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/KGeneratorsDataProvider.java @@ -1,5 +1,7 @@ -package com.volmit.iris.core.link; +package com.volmit.iris.core.link.data; +import com.volmit.iris.core.link.ExternalDataProvider; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.service.ExternalDataSVC; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KMap; @@ -14,6 +16,8 @@ import org.bukkit.block.data.BlockData; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.List; import java.util.MissingResourceException; public class KGeneratorsDataProvider extends ExternalDataProvider { @@ -54,35 +58,17 @@ public class KGeneratorsDataProvider extends ExternalDataProvider { } @Override - public @NotNull Identifier[] getBlockTypes() { + public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) { + if (dataType == DataType.ENTITY) return List.of(); return Main.getGenerators().getAll().stream() .map(gen -> new Identifier("kgenerators", gen.getId())) - .filter(i -> { - try { - return getBlockData(i) != null; - } catch (MissingResourceException e) { - return false; - } - }) - .toArray(Identifier[]::new); + .filter(dataType.asPredicate(this)) + .toList(); } @Override - public @NotNull Identifier[] getItemTypes() { - return Main.getGenerators().getAll().stream() - .map(gen -> new Identifier("kgenerators", gen.getId())) - .filter(i -> { - try { - return getItemStack(i) != null; - } catch (MissingResourceException e) { - return false; - } - }) - .toArray(Identifier[]::new); - } - - @Override - public boolean isValidProvider(@NotNull Identifier id, boolean isItem) { + public boolean isValidProvider(@NotNull Identifier id, DataType dataType) { + if (dataType == DataType.ENTITY) return false; return "kgenerators".equalsIgnoreCase(id.namespace()); } } diff --git a/core/src/main/java/com/volmit/iris/core/link/MMOItemsDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/MMOItemsDataProvider.java similarity index 64% rename from core/src/main/java/com/volmit/iris/core/link/MMOItemsDataProvider.java rename to core/src/main/java/com/volmit/iris/core/link/data/MMOItemsDataProvider.java index 251665a54..0bf3fc490 100644 --- a/core/src/main/java/com/volmit/iris/core/link/MMOItemsDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/MMOItemsDataProvider.java @@ -1,21 +1,24 @@ -package com.volmit.iris.core.link; +package com.volmit.iris.core.link.data; import com.volmit.iris.Iris; -import com.volmit.iris.util.collection.KList; +import com.volmit.iris.core.link.ExternalDataProvider; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.scheduling.J; import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.api.ItemTier; -import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.block.CustomBlock; import org.bukkit.Bukkit; import org.bukkit.block.data.BlockData; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.List; import java.util.MissingResourceException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; public class MMOItemsDataProvider extends ExternalDataProvider { @@ -85,52 +88,35 @@ public class MMOItemsDataProvider extends ExternalDataProvider { return item; } - @NotNull @Override - public Identifier[] getBlockTypes() { - KList names = new KList<>(); - for (Integer id : api().getCustomBlocks().getBlockIds()) { - try { - Identifier key = new Identifier("mmoitems", String.valueOf(id)); - if (getBlockData(key) != null) - names.add(key); - } catch (MissingResourceException ignored) { - } - } - return names.toArray(new Identifier[0]); - } + public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) { + return switch (dataType) { + case ENTITY -> List.of(); + case BLOCK -> api().getCustomBlocks().getBlockIds().stream().map(id -> new Identifier("mmoitems", String.valueOf(id))) + .filter(dataType.asPredicate(this)) + .toList(); + case ITEM -> { + Supplier> supplier = () -> api().getTypes() + .getAll() + .stream() + .flatMap(type -> api() + .getTemplates() + .getTemplateNames(type) + .stream() + .map(name -> new Identifier("mmoitems_" + type.getId(), name))) + .filter(dataType.asPredicate(this)) + .toList(); - @NotNull - @Override - public Identifier[] getItemTypes() { - KList names = new KList<>(); - Runnable run = () -> { - for (Type type : api().getTypes().getAll()) { - for (String name : api().getTemplates().getTemplateNames(type)) { - try { - Identifier key = new Identifier("mmoitems_" + type.getId(), name); - if (getItemStack(key) != null) - names.add(key); - } catch (MissingResourceException ignored) { - } - } + if (Bukkit.isPrimaryThread()) yield supplier.get(); + else yield J.sfut(supplier).join(); } }; - if (Bukkit.isPrimaryThread()) run.run(); - else { - try { - J.sfut(run).get(); - } catch (InterruptedException | ExecutionException e) { - Iris.error("Failed getting MMOItems item types!"); - Iris.reportError(e); - } - } - return names.toArray(new Identifier[0]); } @Override - public boolean isValidProvider(@NotNull Identifier id, boolean isItem) { - return isItem ? id.namespace().split("_", 2).length == 2 : id.namespace().equals("mmoitems"); + public boolean isValidProvider(@NotNull Identifier id, DataType dataType) { + if (dataType == DataType.ENTITY) return false; + return dataType == DataType.ITEM ? id.namespace().split("_", 2).length == 2 : id.namespace().equals("mmoitems"); } private MMOItems api() { diff --git a/core/src/main/java/com/volmit/iris/core/link/MythicCrucibleDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/MythicCrucibleDataProvider.java similarity index 64% rename from core/src/main/java/com/volmit/iris/core/link/MythicCrucibleDataProvider.java rename to core/src/main/java/com/volmit/iris/core/link/data/MythicCrucibleDataProvider.java index d62462c38..4194f0c16 100644 --- a/core/src/main/java/com/volmit/iris/core/link/MythicCrucibleDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/MythicCrucibleDataProvider.java @@ -16,19 +16,18 @@ * along with this program. If not, see . */ -package com.volmit.iris.core.link; +package com.volmit.iris.core.link.data; import com.volmit.iris.Iris; +import com.volmit.iris.core.link.ExternalDataProvider; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.service.ExternalDataSVC; -import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.data.B; import com.volmit.iris.util.data.IrisCustomData; -import com.volmit.iris.util.math.RNG; import io.lumine.mythic.bukkit.BukkitAdapter; import io.lumine.mythic.bukkit.utils.serialize.Chroma; import io.lumine.mythiccrucible.MythicCrucible; @@ -37,11 +36,11 @@ import io.lumine.mythiccrucible.items.ItemManager; import io.lumine.mythiccrucible.items.blocks.CustomBlockItemContext; import io.lumine.mythiccrucible.items.furniture.FurnitureItemContext; import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import java.util.Collection; import java.util.MissingResourceException; import java.util.Optional; @@ -88,69 +87,27 @@ public class MythicCrucibleDataProvider extends ExternalDataProvider { .generateItemStack(1)); } - @NotNull @Override - public Identifier[] getBlockTypes() { - KList names = new KList<>(); - for (CrucibleItem item : this.itemManager.getItems()) { - if (item.getBlockData() == null) continue; - try { - Identifier key = new Identifier("crucible", item.getInternalName()); - if (getBlockData(key) != null) { - Iris.info("getBlockTypes: Block loaded '" + item.getInternalName() + "'"); - names.add(key); - } - } catch (MissingResourceException ignored) {} - } - return names.toArray(new Identifier[0]); - } - - @NotNull - @Override - public Identifier[] getItemTypes() { - KList names = new KList<>(); - for (CrucibleItem item : this.itemManager.getItems()) { - try { - Identifier key = new Identifier("crucible", item.getInternalName()); - if (getItemStack(key) != null) { - Iris.info("getItemTypes: Item loaded '" + item.getInternalName() + "'"); - names.add(key); - } - } catch (MissingResourceException ignored) {} - } - return names.toArray(new Identifier[0]); + public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) { + return itemManager.getItems() + .stream() + .map(i -> new Identifier("crucible", i.getInternalName())) + .filter(dataType.asPredicate(this)) + .toList(); } @Override public void processUpdate(@NotNull Engine engine, @NotNull Block block, @NotNull Identifier blockId) { - var pair = ExternalDataSVC.parseState(blockId); - var state = pair.getB(); - blockId = pair.getA(); + var parsedState = ExternalDataSVC.parseState(blockId); + var state = parsedState.getB(); + blockId = parsedState.getA(); Optional item = itemManager.getItem(blockId.key()); if (item.isEmpty()) return; FurnitureItemContext furniture = item.get().getFurnitureData(); if (furniture == null) return; - float yaw = 0; - BlockFace face = BlockFace.NORTH; - long seed = engine.getSeedManager().getSeed() + Cache.key(block.getX(), block.getZ()) + block.getY(); - RNG rng = new RNG(seed); - if ("true".equals(state.get("randomYaw"))) { - yaw = rng.f(0, 360); - } else if (state.containsKey("yaw")) { - yaw = Float.parseFloat(state.get("yaw")); - } - if ("true".equals(state.get("randomFace"))) { - BlockFace[] faces = BlockFace.values(); - face = faces[rng.i(0, faces.length - 1)]; - } else if (state.containsKey("face")) { - face = BlockFace.valueOf(state.get("face").toUpperCase()); - } - if (face == BlockFace.SELF) { - face = BlockFace.NORTH; - } - + var pair = parseYawAndFace(engine, block, state); BiomeColor type = null; Chroma color = null; try { @@ -161,11 +118,12 @@ public class MythicCrucibleDataProvider extends ExternalDataProvider { if (biomeColor == null) return; color = Chroma.of(biomeColor.getRGB()); } - furniture.place(block, face, yaw, color); + furniture.place(block, pair.getB(), pair.getA(), color); } @Override - public boolean isValidProvider(@NotNull Identifier key, boolean isItem) { + public boolean isValidProvider(@NotNull Identifier key, DataType dataType) { + if (dataType == DataType.ENTITY) return false; return key.namespace().equalsIgnoreCase("crucible"); } } diff --git a/core/src/main/java/com/volmit/iris/core/link/MythicMobsLink.java b/core/src/main/java/com/volmit/iris/core/link/data/MythicMobsDataProvider.java similarity index 54% rename from core/src/main/java/com/volmit/iris/core/link/MythicMobsLink.java rename to core/src/main/java/com/volmit/iris/core/link/data/MythicMobsDataProvider.java index 052952346..7c08b8e6a 100644 --- a/core/src/main/java/com/volmit/iris/core/link/MythicMobsLink.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/MythicMobsDataProvider.java @@ -1,25 +1,7 @@ -/* - * 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 com.volmit.iris.core.link.data; -package com.volmit.iris.core.link; - -import com.google.common.collect.Sets; -import com.volmit.iris.Iris; +import com.volmit.iris.core.link.ExternalDataProvider; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.tools.IrisToolbelt; import io.lumine.mythic.api.adapters.AbstractLocation; import io.lumine.mythic.api.config.MythicLineConfig; @@ -30,60 +12,59 @@ import io.lumine.mythic.bukkit.events.MythicConditionLoadEvent; import io.lumine.mythic.core.skills.SkillCondition; import io.lumine.mythic.core.utils.annotations.MythicCondition; import io.lumine.mythic.core.utils.annotations.MythicField; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; -public class MythicMobsLink { - - public MythicMobsLink() { - if (getPlugin() == null) return; - Iris.instance.registerListener(new ConditionListener()); +public class MythicMobsDataProvider extends ExternalDataProvider { + public MythicMobsDataProvider() { + super("MythicMobs"); } - public boolean isEnabled() { - return getPlugin() != null; + @Override + public void init() { } - public Plugin getPlugin() { - return Bukkit.getPluginManager().getPlugin("MythicMobs"); + @Override + public @Nullable Entity spawnMob(@NotNull Location location, @NotNull Identifier entityId) throws MissingResourceException { + var mm = MythicBukkit.inst().getMobManager().spawnMob(entityId.key(), location); + if (mm == null) throw new MissingResourceException("Failed to find mob!", entityId.namespace(), entityId.key()); + return mm.getEntity().getBukkitEntity(); } - /** - * Spawn a mythic mob at this location - * - * @param mob The mob - * @param location The location - * @return The mob, or null if it can't be spawned - */ - public @Nullable Entity spawnMob(String mob, Location location) { - return isEnabled() ? MythicBukkit.inst().getMobManager().spawnMob(mob, location).getEntity().getBukkitEntity() : null; + @Override + public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) { + if (dataType != DataType.ENTITY) return List.of(); + return MythicBukkit.inst() + .getMobManager() + .getMobNames() + .stream() + .map(name -> new Identifier("mythicmobs", name)) + .toList(); } - public Collection getMythicMobTypes() { - return isEnabled() ? MythicBukkit.inst().getMobManager().getMobNames() : List.of(); + @Override + public boolean isValidProvider(@NotNull Identifier id, DataType dataType) { + return id.namespace().equalsIgnoreCase("mythicmobs") && dataType == DataType.ENTITY; } - private static class ConditionListener implements Listener { - @EventHandler - public void on(MythicConditionLoadEvent event) { - switch (event.getConditionName()) { - case "irisbiome" -> event.register(new IrisBiomeCondition(event.getConditionName(), event.getConfig())); - case "irisregion" -> event.register(new IrisRegionCondition(event.getConditionName(), event.getConfig())); - } + @EventHandler + public void on(MythicConditionLoadEvent event) { + switch (event.getConditionName()) { + case "irisbiome" -> event.register(new IrisBiomeCondition(event.getConditionName(), event.getConfig())); + case "irisregion" -> event.register(new IrisRegionCondition(event.getConditionName(), event.getConfig())); } } @MythicCondition(author = "CrazyDev22", name = "irisbiome", description = "Tests if the target is within the given list of biomes") public static class IrisBiomeCondition extends SkillCondition implements ILocationCondition { @MythicField(name = "biome", aliases = {"b"}, description = "A list of biomes to check") - private Set biomes = Sets.newConcurrentHashSet(); + private Set biomes = ConcurrentHashMap.newKeySet(); @MythicField(name = "surface", aliases = {"s"}, description = "If the biome check should only be performed on the surface") private boolean surface; @@ -107,10 +88,10 @@ public class MythicMobsLink { } } - @MythicCondition(author = "CrazyDev22", name = "irisbiome", description = "Tests if the target is within the given list of biomes") + @MythicCondition(author = "CrazyDev22", name = "irisregion", description = "Tests if the target is within the given list of biomes") public static class IrisRegionCondition extends SkillCondition implements ILocationCondition { @MythicField(name = "region", aliases = {"r"}, description = "A list of regions to check") - private Set regions = Sets.newConcurrentHashSet(); + private Set regions = ConcurrentHashMap.newKeySet(); public IrisRegionCondition(String line, MythicLineConfig mlc) { super(line); diff --git a/core/src/main/java/com/volmit/iris/core/link/NexoDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/NexoDataProvider.java similarity index 63% rename from core/src/main/java/com/volmit/iris/core/link/NexoDataProvider.java rename to core/src/main/java/com/volmit/iris/core/link/data/NexoDataProvider.java index 066fb448b..998fbf06f 100644 --- a/core/src/main/java/com/volmit/iris/core/link/NexoDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/NexoDataProvider.java @@ -1,28 +1,30 @@ -package com.volmit.iris.core.link; +package com.volmit.iris.core.link.data; import com.nexomc.nexo.api.NexoBlocks; import com.nexomc.nexo.api.NexoFurniture; import com.nexomc.nexo.api.NexoItems; import com.nexomc.nexo.items.ItemBuilder; +import com.volmit.iris.core.link.ExternalDataProvider; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.service.ExternalDataSVC; -import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.data.B; import com.volmit.iris.util.data.IrisCustomData; -import com.volmit.iris.util.math.RNG; import org.bukkit.Color; import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; import org.bukkit.entity.ItemDisplay; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.LeatherArmorMeta; +import org.bukkit.inventory.meta.MapMeta; import org.bukkit.inventory.meta.PotionMeta; import org.jetbrains.annotations.NotNull; -import java.util.Arrays; +import java.util.Collection; +import java.util.List; import java.util.MissingResourceException; import java.util.concurrent.atomic.AtomicBoolean; @@ -69,9 +71,9 @@ public class NexoDataProvider extends ExternalDataProvider { @Override public void processUpdate(@NotNull Engine engine, @NotNull Block block, @NotNull Identifier blockId) { - var pair = ExternalDataSVC.parseState(blockId); - var state = pair.getB(); - blockId = pair.getA(); + var statePair = ExternalDataSVC.parseState(blockId); + var state = statePair.getB(); + blockId = statePair.getA(); if (NexoBlocks.isCustomBlock(blockId.key())) { NexoBlocks.place(blockId.key(), block.getLocation()); @@ -81,26 +83,8 @@ public class NexoDataProvider extends ExternalDataProvider { if (!NexoFurniture.isFurniture(blockId.key())) return; - float yaw = 0; - BlockFace face = BlockFace.NORTH; - - long seed = engine.getSeedManager().getSeed() + Cache.key(block.getX(), block.getZ()) + block.getY(); - RNG rng = new RNG(seed); - if ("true".equals(state.get("randomYaw"))) { - yaw = rng.f(0, 360); - } else if (state.containsKey("yaw")) { - yaw = Float.parseFloat(state.get("yaw")); - } - if ("true".equals(state.get("randomFace"))) { - BlockFace[] faces = BlockFace.values(); - face = faces[rng.i(0, faces.length - 1)]; - } else if (state.containsKey("face")) { - face = BlockFace.valueOf(state.get("face").toUpperCase()); - } - if (face == BlockFace.SELF) { - face = BlockFace.NORTH; - } - ItemDisplay display = NexoFurniture.place(blockId.key(), block.getLocation(), yaw, face); + var pair = parseYawAndFace(engine, block, state); + ItemDisplay display = NexoFurniture.place(blockId.key(), block.getLocation(), pair.getA(), pair.getB()); if (display == null) return; ItemStack itemStack = display.getItemStack(); if (itemStack == null) return; @@ -114,46 +98,31 @@ public class NexoDataProvider extends ExternalDataProvider { var biomeColor = INMS.get().getBiomeColor(block.getLocation(), type); if (biomeColor == null) return; var potionColor = Color.fromARGB(biomeColor.getAlpha(), biomeColor.getRed(), biomeColor.getGreen(), biomeColor.getBlue()); - if (itemStack.getItemMeta() instanceof PotionMeta meta) { - meta.setColor(potionColor); - itemStack.setItemMeta(meta); + var meta = itemStack.getItemMeta(); + switch (meta) { + case LeatherArmorMeta armor -> armor.setColor(potionColor); + case PotionMeta potion -> potion.setColor(potionColor); + case MapMeta map -> map.setColor(potionColor); + case null, default -> {} } + itemStack.setItemMeta(meta); } display.setItemStack(itemStack); } - @NotNull @Override - public Identifier[] getBlockTypes() { - return NexoItems.itemNames().stream() + public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) { + if (dataType == DataType.ENTITY) return List.of(); + return NexoItems.itemNames() + .stream() .map(i -> new Identifier("nexo", i)) - .filter(i -> { - try { - return getBlockData(i) != null; - } catch (MissingResourceException e) { - return false; - } - }) - .toArray(Identifier[]::new); - } - - @NotNull - @Override - public Identifier[] getItemTypes() { - return NexoItems.itemNames().stream() - .map(i -> new Identifier("nexo", i)) - .filter(i -> { - try { - return getItemStack(i) != null; - } catch (MissingResourceException e) { - return false; - } - }) - .toArray(Identifier[]::new); + .filter(dataType.asPredicate(this)) + .toList(); } @Override - public boolean isValidProvider(@NotNull Identifier id, boolean isItem) { + public boolean isValidProvider(@NotNull Identifier id, DataType dataType) { + if (dataType == DataType.ENTITY) return false; return "nexo".equalsIgnoreCase(id.namespace()); } diff --git a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java index d856d2b8e..50ff64886 100644 --- a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java +++ b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java @@ -19,9 +19,12 @@ package com.volmit.iris.core.project; import com.volmit.iris.Iris; +import com.volmit.iris.core.link.Identifier; +import com.volmit.iris.core.link.data.DataType; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisRegistrant; import com.volmit.iris.core.loader.ResourceLoader; +import com.volmit.iris.core.service.ExternalDataSVC; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; @@ -39,7 +42,6 @@ import java.lang.reflect.Modifier; import java.util.List; import java.util.Map; import java.util.function.Function; -import java.util.stream.Collectors; public class SchemaBuilder { private static final String SYMBOL_LIMIT__N = "*"; @@ -266,16 +268,18 @@ public class SchemaBuilder { if (!definitions.containsKey(key)) { JSONObject j = new JSONObject(); - KList list = new KList<>(); - list.addAll(Iris.linkMythicMobs.getMythicMobTypes().stream().map(s -> "MythicMobs:" + s).collect(Collectors.toList())); - //TODO add Citizens stuff here too + KList list = Iris.service(ExternalDataSVC.class) + .getAllIdentifiers(DataType.ENTITY) + .stream() + .map(Identifier::toString) + .collect(KList.collector()); j.put("enum", list.toJSONStringArray()); definitions.put(key, j); } - fancyType = "Mythic Mob Type"; + fancyType = "Custom Mob Type"; prop.put("$ref", "#/definitions/" + key); - description.add(SYMBOL_TYPE__N + " Must be a valid Mythic Mob Type (use ctrl+space for auto complete!) Define mythic mobs with the mythic mobs plugin configuration files."); + description.add(SYMBOL_TYPE__N + " Must be a valid Custom Mob Type (use ctrl+space for auto complete!)"); } else if (k.isAnnotationPresent(RegistryListFont.class)) { String key = "enum-font"; diff --git a/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java b/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java index 4d9596278..69fec1912 100644 --- a/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java @@ -20,16 +20,21 @@ package com.volmit.iris.core.service; import com.volmit.iris.Iris; import com.volmit.iris.core.link.*; +import com.volmit.iris.core.link.data.DataType; import com.volmit.iris.core.nms.container.Pair; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.io.JarScanner; import com.volmit.iris.util.plugin.IrisService; +import com.volmit.iris.util.scheduling.J; import lombok.Data; import lombok.NonNull; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; import org.bukkit.event.server.PluginEnableEvent; import org.bukkit.inventory.ItemStack; @@ -47,43 +52,12 @@ public class ExternalDataSVC implements IrisService { Iris.info("Loading ExternalDataProvider..."); Bukkit.getPluginManager().registerEvents(this, Iris.instance); - providers.add(new NexoDataProvider()); - if (Bukkit.getPluginManager().getPlugin("Nexo") != null) { - Iris.info("Nexo found, loading NexoDataProvider..."); - } - providers.add(new MythicCrucibleDataProvider()); - if (Bukkit.getPluginManager().getPlugin("MythicCrucible") != null) { - Iris.info("MythicCrucible found, loading MythicCrucibleDataProvider..."); - } - providers.add(new ItemAdderDataProvider()); - if (Bukkit.getPluginManager().getPlugin("ItemAdder") != null) { - Iris.info("ItemAdder found, loading ItemAdderDataProvider..."); - } - providers.add(new ExecutableItemsDataProvider()); - if (Bukkit.getPluginManager().getPlugin("ExecutableItems") != null) { - Iris.info("ExecutableItems found, loading ExecutableItemsDataProvider..."); - } - providers.add(new HMCLeavesDataProvider()); - if (Bukkit.getPluginManager().getPlugin("HMCLeaves") != null) { - Iris.info("BlockAdder found, loading HMCLeavesDataProvider..."); - } - providers.add(new MMOItemsDataProvider()); - if (Bukkit.getPluginManager().getPlugin("MMOItems") != null) { - Iris.info("MMOItems found, loading MMOItemsDataProvider..."); - } - providers.add(new EcoItemsDataProvider()); - if (Bukkit.getPluginManager().getPlugin("EcoItems") != null) { - Iris.info("EcoItems found, loading EcoItemsDataProvider..."); - } - providers.add(new KGeneratorsDataProvider()); - if (Bukkit.getPluginManager().getPlugin("KGenerators") != null) { - Iris.info("KGenerators found, loading KGeneratorsDataProvider..."); - } - + providers.addAll(createProviders()); for (ExternalDataProvider p : providers) { if (p.isReady()) { activeProviders.add(p); p.init(); + Iris.instance.registerListener(p); Iris.info("Enabled ExternalDataProvider for %s.", p.getPluginId()); } } @@ -99,6 +73,7 @@ public class ExternalDataSVC implements IrisService { providers.stream().filter(p -> p.isReady() && p.getPlugin().equals(e.getPlugin())).findFirst().ifPresent(edp -> { activeProviders.add(edp); edp.init(); + Iris.instance.registerListener(edp); Iris.info("Enabled ExternalDataProvider for %s.", edp.getPluginId()); }); } @@ -113,6 +88,7 @@ public class ExternalDataSVC implements IrisService { if (provider.isReady()) { activeProviders.add(provider); provider.init(); + Iris.instance.registerListener(provider); } } @@ -120,7 +96,7 @@ public class ExternalDataSVC implements IrisService { var pair = parseState(key); Identifier mod = pair.getA(); - Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(mod, false)).findFirst(); + Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(mod, DataType.BLOCK)).findFirst(); if (provider.isEmpty()) return Optional.empty(); try { @@ -132,7 +108,7 @@ public class ExternalDataSVC implements IrisService { } public Optional getItemStack(Identifier key, KMap customNbt) { - Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(key, true)).findFirst(); + Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(key, DataType.ITEM)).findFirst(); if (provider.isEmpty()) { Iris.warn("No matching Provider found for modded material \"%s\"!", key); return Optional.empty(); @@ -146,7 +122,7 @@ public class ExternalDataSVC implements IrisService { } public void processUpdate(Engine engine, Block block, Identifier blockId) { - Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(blockId, false)).findFirst(); + Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(blockId, DataType.BLOCK)).findFirst(); if (provider.isEmpty()) { Iris.warn("No matching Provider found for modded material \"%s\"!", blockId); return; @@ -154,16 +130,24 @@ public class ExternalDataSVC implements IrisService { provider.get().processUpdate(engine, block, blockId); } - public Identifier[] getAllBlockIdentifiers() { - KList names = new KList<>(); - activeProviders.forEach(p -> names.add(p.getBlockTypes())); - return names.toArray(new Identifier[0]); + public Entity spawnMob(Location location, Identifier mobId) { + Optional provider = activeProviders.stream().filter(p -> p.isValidProvider(mobId, DataType.ENTITY)).findFirst(); + if (provider.isEmpty()) { + Iris.warn("No matching Provider found for modded mob \"%s\"!", mobId); + return null; + } + try { + return provider.get().spawnMob(location, mobId); + } catch (MissingResourceException e) { + Iris.error(e.getMessage() + " - [" + e.getClassName() + ":" + e.getKey() + "]"); + return null; + } } - public Identifier[] getAllItemIdentifiers() { - KList names = new KList<>(); - activeProviders.forEach(p -> names.add(p.getItemTypes())); - return names.toArray(new Identifier[0]); + public Collection getAllIdentifiers(DataType dataType) { + return activeProviders.stream() + .flatMap(p -> p.getTypes(dataType).stream()) + .toList(); } public static Pair> parseState(Identifier key) { @@ -188,4 +172,21 @@ public class ExternalDataSVC implements IrisService { .collect(Collectors.joining(",", key.key() + "[", "]")); return new Identifier(key.namespace(), path); } + + private static KList createProviders() { + JarScanner jar = new JarScanner(Iris.instance.getJarFile(), "com.volmit.iris.core.link.data"); + J.attempt(jar::scan); + KList providers = new KList<>(); + + for (Class c : jar.getClasses()) { + if (ExternalDataProvider.class.isAssignableFrom(c)) { + try { + ExternalDataProvider p = (ExternalDataProvider) c.getDeclaredConstructor().newInstance(); + if (p.getPlugin() != null) Iris.info(p.getPluginId() + " found, loading " + c.getSimpleName() + "..."); + providers.add(p); + } catch (Throwable ignored) {} + } + } + return providers; + } } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisEntity.java b/core/src/main/java/com/volmit/iris/engine/object/IrisEntity.java index dfb2508d0..a87c319d0 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisEntity.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisEntity.java @@ -20,8 +20,10 @@ package com.volmit.iris.engine.object; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.loader.IrisRegistrant; import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.core.service.ExternalDataSVC; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.util.collection.KList; @@ -455,24 +457,13 @@ public class IrisEntity extends IrisRegistrant { } if (isSpecialType()) { - if (specialType.toLowerCase().startsWith("mythicmobs:")) { - return Iris.linkMythicMobs.spawnMob(specialType.substring(11), at); - } else { - Iris.warn("Invalid mob type to spawn: '" + specialType + "'!"); - return null; - } + return Iris.service(ExternalDataSVC.class).spawnMob(at, Identifier.fromString(specialType)); } return INMS.get().spawnEntity(at, getType(), getReason()); } - public boolean isCitizens() { - return false; - - // TODO: return Iris.linkCitizens.supported() && someType is not empty; - } - public boolean isSpecialType() { return specialType != null && !specialType.equals(""); } diff --git a/core/src/main/java/com/volmit/iris/util/data/B.java b/core/src/main/java/com/volmit/iris/util/data/B.java index 1c7a521cb..7e71d39f3 100644 --- a/core/src/main/java/com/volmit/iris/util/data/B.java +++ b/core/src/main/java/com/volmit/iris/util/data/B.java @@ -21,6 +21,7 @@ package com.volmit.iris.util.data; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.link.Identifier; +import com.volmit.iris.core.link.data.DataType; import com.volmit.iris.core.service.ExternalDataSVC; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; @@ -673,7 +674,7 @@ public class B { } } - for (Identifier id : Iris.service(ExternalDataSVC.class).getAllBlockIdentifiers()) + for (Identifier id : Iris.service(ExternalDataSVC.class).getAllIdentifiers(DataType.BLOCK)) bt.add(id.toString()); bt.addAll(custom.k()); @@ -688,7 +689,7 @@ public class B { bt.add(v); } - for (Identifier id : Iris.service(ExternalDataSVC.class).getAllItemIdentifiers()) + for (Identifier id : Iris.service(ExternalDataSVC.class).getAllIdentifiers(DataType.ITEM)) bt.add(id.toString()); return bt.toArray(new String[0]); diff --git a/core/src/main/java/com/volmit/iris/util/scheduling/J.java b/core/src/main/java/com/volmit/iris/util/scheduling/J.java index a669317df..550b94092 100644 --- a/core/src/main/java/com/volmit/iris/util/scheduling/J.java +++ b/core/src/main/java/com/volmit/iris/util/scheduling/J.java @@ -240,6 +240,21 @@ public class J { return f; } + public static CompletableFuture sfut(Supplier r) { + CompletableFuture f = new CompletableFuture<>(); + if (!Bukkit.getPluginManager().isPluginEnabled(Iris.instance)) { + return null; + } + Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> { + try { + f.complete(r.get()); + } catch (Throwable e) { + f.completeExceptionally(e); + } + }); + return f; + } + public static CompletableFuture sfut(Runnable r, int delay) { CompletableFuture f = new CompletableFuture(); From 387e8adfe2639de66464fed00ed7b584c58b9433 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 12 Jul 2025 18:10:39 +0200 Subject: [PATCH 133/138] ignore errors when scanning for external data providers --- .../main/java/com/volmit/iris/core/service/ExternalDataSVC.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java b/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java index 69fec1912..49303b60c 100644 --- a/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java @@ -174,7 +174,7 @@ public class ExternalDataSVC implements IrisService { } private static KList createProviders() { - JarScanner jar = new JarScanner(Iris.instance.getJarFile(), "com.volmit.iris.core.link.data"); + JarScanner jar = new JarScanner(Iris.instance.getJarFile(), "com.volmit.iris.core.link.data", false); J.attempt(jar::scan); KList providers = new KList<>(); From 106a3834ab96707a19a46c4a21633a642555b556 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 13 Jul 2025 17:42:10 +0200 Subject: [PATCH 134/138] fix iris create command failing --- .../iris/core/commands/CommandIris.java | 1279 ++++++++--------- 1 file changed, 631 insertions(+), 648 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java index 4723ed3e1..38e2266e3 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java @@ -1,648 +1,631 @@ -/* - * 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 com.volmit.iris.core.commands; - -import com.volmit.iris.Iris; -import com.volmit.iris.core.IrisSettings; -import com.volmit.iris.core.loader.IrisData; -import com.volmit.iris.core.nms.INMS; -import com.volmit.iris.core.pregenerator.ChunkUpdater; -import com.volmit.iris.core.service.StudioSVC; -import com.volmit.iris.core.tools.IrisToolbelt; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.object.IrisDimension; -import com.volmit.iris.core.safeguard.UtilsSFG; -import com.volmit.iris.engine.object.IrisWorld; -import com.volmit.iris.engine.platform.BukkitChunkGenerator; -import com.volmit.iris.engine.platform.DummyChunkGenerator; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.decree.DecreeExecutor; -import com.volmit.iris.util.decree.DecreeOrigin; -import com.volmit.iris.util.decree.annotations.Decree; -import com.volmit.iris.util.decree.annotations.Param; -import com.volmit.iris.util.decree.specialhandlers.NullablePlayerHandler; -import com.volmit.iris.util.format.C; -import com.volmit.iris.util.format.Form; -import com.volmit.iris.util.plugin.VolmitSender; -import com.volmit.iris.util.scheduling.J; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.WorldCreator; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.generator.ChunkGenerator; -import org.bukkit.scheduler.BukkitRunnable; - -import java.io.File; -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -import static com.volmit.iris.Iris.service; -import static com.volmit.iris.core.service.EditSVC.deletingWorld; -import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode; -import static com.volmit.iris.core.safeguard.ServerBootSFG.incompatibilities; -import static org.bukkit.Bukkit.getServer; - -@Decree(name = "iris", aliases = {"ir", "irs"}, description = "Basic Command") -public class CommandIris implements DecreeExecutor { - private CommandStudio studio; - private CommandPregen pregen; - private CommandSettings settings; - private CommandObject object; - private CommandJigsaw jigsaw; - private CommandWhat what; - private CommandEdit edit; - private CommandFind find; - private CommandDeveloper developer; - public static boolean worldCreation = false; - String WorldEngine; - String worldNameToCheck = "YourWorldName"; - VolmitSender sender = Iris.getSender(); - - @Decree(description = "Create a new world", aliases = {"+", "c"}) - public void create( - @Param(aliases = "world-name", description = "The name of the world to create") - String name, - @Param(aliases = "dimension", description = "The dimension type to create the world with", defaultValue = "default") - IrisDimension type, - @Param(description = "The seed to generate the world with", defaultValue = "1337") - long seed - ) { - if(sender() instanceof Player) { - if (incompatibilities.get("Multiverse-Core")) { - sender().sendMessage(C.RED + "Your server has an incompatibility that may corrupt all worlds on the server if not handled properly."); - sender().sendMessage(C.RED + "it is strongly advised for you to take action. see log for full detail"); - sender().sendMessage(C.RED + "----------------------------------------------------------------"); - sender().sendMessage(C.RED + "Command ran: /iris create"); - sender().sendMessage(C.RED + UtilsSFG.MSGIncompatibleWarnings()); - sender().sendMessage(C.RED + "----------------------------------------------------------------"); - } - if (unstablemode && !incompatibilities.get("Multiverse-Core")) { - sender().sendMessage(C.RED + "Your server is experiencing an incompatibility with the Iris plugin."); - sender().sendMessage(C.RED + "Please rectify this problem to avoid further complications."); - sender().sendMessage(C.RED + "----------------------------------------------------------------"); - sender().sendMessage(C.RED + "Command ran: /iris create"); - sender().sendMessage(C.RED + UtilsSFG.MSGIncompatibleWarnings()); - sender().sendMessage(C.RED + "----------------------------------------------------------------"); - } - } - if (name.equals("iris")) { - sender().sendMessage(C.RED + "You cannot use the world name \"iris\" for creating worlds as Iris uses this directory for studio worlds."); - sender().sendMessage(C.RED + "May we suggest the name \"IrisWorld\" instead?"); - return; - } - if (name.equals("Benchmark")) { - sender().sendMessage(C.RED + "You cannot use the world name \"Benchmark\" for creating worlds as Iris uses this directory for Benchmarking Packs."); - sender().sendMessage(C.RED + "May we suggest the name \"IrisWorld\" instead?"); - return; - } - - if (new File(Bukkit.getWorldContainer(), name).exists()) { - sender().sendMessage(C.RED + "That folder already exists!"); - return; - } - - try { - worldCreation = true; - IrisToolbelt.createWorld() - .dimension(type.getLoadKey()) - .name(name) - .seed(seed) - .sender(sender()) - .studio(false) - .create(); - } catch (Throwable e) { - sender().sendMessage(C.RED + "Exception raised during creation. See the console for more details."); - Iris.error("Exception raised during world creation: " + e.getMessage()); - Iris.reportError(e); - worldCreation = false; - return; - } - worldCreation = false; - sender().sendMessage(C.GREEN + "Successfully created your world!"); - } - - @Decree(description = "Teleport to another world", aliases = {"tp"}, sync = true) - public void teleport( - @Param(description = "World to teleport to") - World world, - @Param(description = "Player to teleport", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player - ) { - if (player == null && sender().isPlayer()) - player = sender().player(); - - final Player target = player; - if (target == null) { - sender().sendMessage(C.RED + "The specified player does not exist."); - return; - } - - new BukkitRunnable() { - @Override - public void run() { - target.teleport(world.getSpawnLocation()); - new VolmitSender(target).sendMessage(C.GREEN + "You have been teleported to " + world.getName() + "."); - } - }.runTask(Iris.instance); - } - - @Decree(description = "Print version information") - public void version() { - sender().sendMessage(C.GREEN + "Iris v" + Iris.instance.getDescription().getVersion() + " by Volmit Software"); - } - - /* - /todo - @Decree(description = "Benchmark a pack", origin = DecreeOrigin.CONSOLE) - public void packbenchmark( - @Param(description = "Dimension to benchmark") - IrisDimension type - ) throws InterruptedException { - - BenchDimension = type.getLoadKey(); - - IrisPackBenchmarking.runBenchmark(); - } */ - - @Decree(description = "Print world height information", origin = DecreeOrigin.PLAYER) - public void height() { - if (sender().isPlayer()) { - sender().sendMessage(C.GREEN + "" + sender().player().getWorld().getMinHeight() + " to " + sender().player().getWorld().getMaxHeight()); - sender().sendMessage(C.GREEN + "Total Height: " + (sender().player().getWorld().getMaxHeight() - sender().player().getWorld().getMinHeight())); - } else { - World mainWorld = getServer().getWorlds().get(0); - Iris.info(C.GREEN + "" + mainWorld.getMinHeight() + " to " + mainWorld.getMaxHeight()); - Iris.info(C.GREEN + "Total Height: " + (mainWorld.getMaxHeight() - mainWorld.getMinHeight())); - } - } - - @Decree(description = "QOL command to open a overworld studio world.", sync = true) - public void so() { - sender().sendMessage(C.GREEN + "Opening studio for the \"Overworld\" pack (seed: 1337)"); - Iris.service(StudioSVC.class).open(sender(), 1337, "overworld"); - } - - @Decree(description = "Check access of all worlds.", aliases = {"accesslist"}) - public void worlds() { - KList IrisWorlds = new KList<>(); - KList BukkitWorlds = new KList<>(); - - for (World w : Bukkit.getServer().getWorlds()) { - try { - Engine engine = IrisToolbelt.access(w).getEngine(); - if (engine != null) { - IrisWorlds.add(w); - } - } catch (Exception e) { - BukkitWorlds.add(w); - } - } - - if (sender().isPlayer()) { - sender().sendMessage(C.BLUE + "Iris Worlds: "); - for (World IrisWorld : IrisWorlds.copy()) { - sender().sendMessage(C.IRIS + "- " +IrisWorld.getName()); - } - sender().sendMessage(C.GOLD + "Bukkit Worlds: "); - for (World BukkitWorld : BukkitWorlds.copy()) { - sender().sendMessage(C.GRAY + "- " +BukkitWorld.getName()); - } - } else { - Iris.info(C.BLUE + "Iris Worlds: "); - for (World IrisWorld : IrisWorlds.copy()) { - Iris.info(C.IRIS + "- " +IrisWorld.getName()); - } - Iris.info(C.GOLD + "Bukkit Worlds: "); - for (World BukkitWorld : BukkitWorlds.copy()) { - Iris.info(C.GRAY + "- " +BukkitWorld.getName()); - } - - } - } - - @Decree(description = "Remove an Iris world", aliases = {"del", "rm", "delete"}, sync = true) - public void remove( - @Param(description = "The world to remove") - World world, - @Param(description = "Whether to also remove the folder (if set to false, just does not load the world)", defaultValue = "true") - boolean delete - ) { - if (!IrisToolbelt.isIrisWorld(world)) { - sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); - return; - } - sender().sendMessage(C.GREEN + "Removing world: " + world.getName()); - - if (!IrisToolbelt.evacuate(world)) { - sender().sendMessage(C.RED + "Failed to evacuate world: " + world.getName()); - return; - } - - if (!Bukkit.unloadWorld(world, false)) { - sender().sendMessage(C.RED + "Failed to unload world: " + world.getName()); - return; - } - - try { - if (IrisToolbelt.removeWorld(world)) { - sender().sendMessage(C.GREEN + "Successfully removed " + world.getName() + " from bukkit.yml"); - } else { - sender().sendMessage(C.YELLOW + "Looks like the world was already removed from bukkit.yml"); - } - } catch (IOException e) { - sender().sendMessage(C.RED + "Failed to save bukkit.yml because of " + e.getMessage()); - e.printStackTrace(); - } - IrisToolbelt.evacuate(world, "Deleting world"); - deletingWorld = true; - if (!delete) { - deletingWorld = false; - return; - } - VolmitSender sender = sender(); - J.a(() -> { - int retries = 12; - - if (deleteDirectory(world.getWorldFolder())) { - sender.sendMessage(C.GREEN + "Successfully removed world folder"); - } else { - while(true){ - if (deleteDirectory(world.getWorldFolder())){ - sender.sendMessage(C.GREEN + "Successfully removed world folder"); - break; - } - retries--; - if (retries == 0){ - sender.sendMessage(C.RED + "Failed to remove world folder"); - break; - } - J.sleep(3000); - } - } - deletingWorld = false; - }); - } - - public static boolean deleteDirectory(File dir) { - if (dir.isDirectory()) { - File[] children = dir.listFiles(); - for (int i = 0; i < children.length; i++) { - boolean success = deleteDirectory(children[i]); - if (!success) { - return false; - } - } - } - return dir.delete(); - } - - @Decree(description = "Updates all chunk in the specified world") - public void updater( - @Param(description = "World to update chunks at") - World world - ) { - if (!IrisToolbelt.isIrisWorld(world)) { - sender().sendMessage(C.GOLD + "This is not an Iris world"); - return; - } - ChunkUpdater updater = new ChunkUpdater(world); - if (sender().isPlayer()) { - sender().sendMessage(C.GREEN + "Updating " + world.getName() + " Total chunks: " + Form.f(updater.getChunks())); - } else { - Iris.info(C.GREEN + "Updating " + world.getName() + " Total chunks: " + Form.f(updater.getChunks())); - } - updater.start(); - } - - @Decree(description = "Set aura spins") - public void aura( - @Param(description = "The h color value", defaultValue = "-20") - int h, - @Param(description = "The s color value", defaultValue = "7") - int s, - @Param(description = "The b color value", defaultValue = "8") - int b - ) { - IrisSettings.get().getGeneral().setSpinh(h); - IrisSettings.get().getGeneral().setSpins(s); - IrisSettings.get().getGeneral().setSpinb(b); - IrisSettings.get().forceSave(); - sender().sendMessage("Aura Spins updated to " + h + " " + s + " " + b); - } - - @Decree(description = "Bitwise calculations") - public void bitwise( - @Param(description = "The first value to run calculations on") - int value1, - @Param(description = "The operator: | & ^ ≺≺ ≻≻ %") - String operator, - @Param(description = "The second value to run calculations on") - int value2 - ) { - Integer v = null; - switch (operator) { - case "|" -> v = value1 | value2; - case "&" -> v = value1 & value2; - case "^" -> v = value1 ^ value2; - case "%" -> v = value1 % value2; - case ">>" -> v = value1 >> value2; - case "<<" -> v = value1 << value2; - } - if (v == null) { - sender().sendMessage(C.RED + "The operator you entered: (" + operator + ") is invalid!"); - return; - } - sender().sendMessage(C.GREEN + "" + value1 + " " + C.GREEN + operator.replaceAll("<", "≺").replaceAll(">", "≻").replaceAll("%", "%") + " " + C.GREEN + value2 + C.GREEN + " returns " + C.GREEN + v); - } - - @Decree(description = "Toggle debug") - public void debug( - @Param(name = "on", description = "Whether or not debug should be on", defaultValue = "other") - Boolean on - ) { - boolean to = on == null ? !IrisSettings.get().getGeneral().isDebug() : on; - IrisSettings.get().getGeneral().setDebug(to); - IrisSettings.get().forceSave(); - sender().sendMessage(C.GREEN + "Set debug to: " + to); - } - - @Decree(description = "Download a project.", aliases = "dl") - public void download( - @Param(name = "pack", description = "The pack to download", defaultValue = "overworld", aliases = "project") - String pack, - @Param(name = "branch", description = "The branch to download from", defaultValue = "main") - String branch, - @Param(name = "trim", description = "Whether or not to download a trimmed version (do not enable when editing)", defaultValue = "false") - boolean trim, - @Param(name = "overwrite", description = "Whether or not to overwrite the pack with the downloaded one", aliases = "force", defaultValue = "false") - boolean overwrite - ) { - sender().sendMessage(C.GREEN + "Downloading pack: " + pack + "/" + branch + (trim ? " trimmed" : "") + (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, trim, overwrite); - } else { - Iris.service(StudioSVC.class).downloadSearch(sender(), "IrisDimensions/" + pack + "/" + branch, trim, overwrite); - } - } - - @Decree(description = "Get metrics for your world", aliases = "measure", origin = DecreeOrigin.PLAYER) - public void metrics() { - if (!IrisToolbelt.isIrisWorld(world())) { - sender().sendMessage(C.RED + "You must be in an Iris world"); - return; - } - sender().sendMessage(C.GREEN + "Sending metrics..."); - engine().printMetrics(sender()); - } - - @Decree(description = "Reload configuration file (this is also done automatically)") - public void reload() { - IrisSettings.invalidate(); - IrisSettings.get(); - sender().sendMessage(C.GREEN + "Hotloaded settings"); - } - - @Decree(description = "Update the pack of a world (UNSAFE!)", name = "^world", aliases = "update-world") - public void updateWorld( - @Param(description = "The world to update", contextual = true) - World world, - @Param(description = "The pack to install into the world", contextual = true, aliases = "dimension") - IrisDimension pack, - @Param(description = "Make sure to make a backup & read the warnings first!", defaultValue = "false", aliases = "c") - boolean confirm, - @Param(description = "Should Iris download the pack again for you", defaultValue = "false", name = "fresh-download", aliases = {"fresh", "new"}) - boolean freshDownload - ) { - if (!confirm) { - sender().sendMessage(new String[]{ - C.RED + "You should always make a backup before using this", - C.YELLOW + "Issues caused by this can be, but are not limited to:", - C.YELLOW + " - Broken chunks (cut-offs) between old and new chunks (before & after the update)", - C.YELLOW + " - Regenerated chunks that do not fit in with the old chunks", - C.YELLOW + " - Structures not spawning again when regenerating", - C.YELLOW + " - Caves not lining up", - C.YELLOW + " - Terrain layers not lining up", - C.RED + "Now that you are aware of the risks, and have made a back-up:", - C.RED + "/iris ^world " + world.getName() + " " + pack.getLoadKey() + " confirm=true" - }); - return; - } - - File folder = world.getWorldFolder(); - folder.mkdirs(); - - if (freshDownload) { - Iris.service(StudioSVC.class).downloadSearch(sender(), pack.getLoadKey(), false, true); - } - - Iris.service(StudioSVC.class).installIntoWorld(sender(), pack.getLoadKey(), folder); - } - - @Decree(description = "Unload an Iris World", origin = DecreeOrigin.PLAYER, sync = true) - public void unloadWorld( - @Param(description = "The world to unload") - World world - ) { - if (!IrisToolbelt.isIrisWorld(world)) { - sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); - return; - } - sender().sendMessage(C.GREEN + "Unloading world: " + world.getName()); - try { - IrisToolbelt.evacuate(world); - Bukkit.unloadWorld(world, false); - sender().sendMessage(C.GREEN + "World unloaded successfully."); - } catch (Exception e) { - sender().sendMessage(C.RED + "Failed to unload the world: " + e.getMessage()); - e.printStackTrace(); - } - } - - @Decree(description = "Load an Iris World", origin = DecreeOrigin.PLAYER, sync = true, aliases = {"import"}) - public void loadWorld( - @Param(description = "The name of the world to load") - String world - ) { - World worldloaded = Bukkit.getWorld(world); - worldNameToCheck = world; - boolean worldExists = doesWorldExist(worldNameToCheck); - WorldEngine = world; - - if (!worldExists) { - sender().sendMessage(C.YELLOW + world + " Doesnt exist on the server."); - return; - } - - File BUKKIT_YML = new File("bukkit.yml"); - String pathtodim = world + File.separator +"iris"+File.separator +"pack"+File.separator +"dimensions"+File.separator; - File directory = new File(Bukkit.getWorldContainer(), pathtodim); - - String dimension = null; - if (directory.exists() && directory.isDirectory()) { - File[] files = directory.listFiles(); - if (files != null) { - for (File file : files) { - if (file.isFile()) { - String fileName = file.getName(); - if (fileName.endsWith(".json")) { - dimension = fileName.substring(0, fileName.length() - 5); - sender().sendMessage(C.BLUE + "Generator: " + dimension); - } - } - } - } - } else { - sender().sendMessage(C.GOLD + world + " is not an iris world."); - return; - } - sender().sendMessage(C.GREEN + "Loading world: " + world); - - YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML); - String gen = "Iris:" + dimension; - ConfigurationSection section = yml.contains("worlds") ? yml.getConfigurationSection("worlds") : yml.createSection("worlds"); - if (!section.contains(world)) { - section.createSection(world).set("generator", gen); - try { - yml.save(BUKKIT_YML); - Iris.info("Registered \"" + world + "\" in bukkit.yml"); - } catch (IOException e) { - Iris.error("Failed to update bukkit.yml!"); - e.printStackTrace(); - return; - } - } - checkForBukkitWorlds(world); - sender().sendMessage(C.GREEN + world + " loaded successfully."); - } - @Decree(description = "Evacuate an iris world", origin = DecreeOrigin.PLAYER, sync = true) - public void evacuate( - @Param(description = "Evacuate the world") - World world - ) { - if (!IrisToolbelt.isIrisWorld(world)) { - sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); - return; - } - sender().sendMessage(C.GREEN + "Evacuating world" + world.getName()); - IrisToolbelt.evacuate(world); - } - - boolean doesWorldExist(String worldName) { - File worldContainer = Bukkit.getWorldContainer(); - File worldDirectory = new File(worldContainer, worldName); - return worldDirectory.exists() && worldDirectory.isDirectory(); - } - private void checkForBukkitWorlds(String world) { - FileConfiguration fc = new YamlConfiguration(); - try { - fc.load(new File("bukkit.yml")); - ConfigurationSection section = fc.getConfigurationSection("worlds"); - if (section == null) { - return; - } - - List worldsToLoad = Collections.singletonList(world); - - for (String s : section.getKeys(false)) { - if (!worldsToLoad.contains(s)) { - continue; - } - ConfigurationSection entry = section.getConfigurationSection(s); - if (!entry.contains("generator", true)) { - continue; - } - String generator = entry.getString("generator"); - if (generator.startsWith("Iris:")) { - generator = generator.split("\\Q:\\E")[1]; - } else if (generator.equalsIgnoreCase("Iris")) { - generator = IrisSettings.get().getGenerator().getDefaultWorldType(); - } else { - continue; - } - Iris.info("2 World: %s | Generator: %s", s, generator); - if (Bukkit.getWorlds().stream().anyMatch(w -> w.getName().equals(s))) { - continue; - } - Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "..."); - WorldCreator c = new WorldCreator(s) - .generator(getDefaultWorldGenerator(s, generator)) - .environment(IrisData.loadAnyDimension(generator).getEnvironment()); - INMS.get().createWorld(c); - Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!"); - } - } catch (Throwable e) { - e.printStackTrace(); - } - } - public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { - Iris.debug("Default World Generator Called for " + worldName + " using ID: " + id); - if (worldName.equals("test")) { - try { - throw new RuntimeException(); - } catch (Throwable e) { - Iris.info(e.getStackTrace()[1].getClassName()); - if (e.getStackTrace()[1].getClassName().contains("com.onarandombox.MultiverseCore")) { - Iris.debug("MVC Test detected, Quick! Send them the dummy!"); - return new DummyChunkGenerator(); - } - } - } - IrisDimension dim; - if (id == null || id.isEmpty()) { - dim = IrisData.loadAnyDimension(IrisSettings.get().getGenerator().getDefaultWorldType()); - } else { - dim = IrisData.loadAnyDimension(id); - } - Iris.debug("Generator ID: " + id + " requested by bukkit/plugin"); - - if (dim == null) { - Iris.warn("Unable to find dimension type " + id + " Looking for online packs..."); - - service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, true); - dim = IrisData.loadAnyDimension(id); - - if (dim == null) { - throw new RuntimeException("Can't find dimension " + id + "!"); - } else { - Iris.info("Resolved missing dimension, proceeding with generation."); - } - } - Iris.debug("Assuming IrisDimension: " + dim.getName()); - IrisWorld w = IrisWorld.builder() - .name(worldName) - .seed(1337) - .environment(dim.getEnvironment()) - .worldFolder(new File(Bukkit.getWorldContainer(), worldName)) - .minHeight(dim.getMinHeight()) - .maxHeight(dim.getMaxHeight()) - .build(); - Iris.debug("Generator Config: " + w.toString()); - File ff = new File(w.worldFolder(), "iris/pack"); - if (!ff.exists() || ff.listFiles().length == 0) { - ff.mkdirs(); - service(StudioSVC.class).installIntoWorld(sender, dim.getLoadKey(), ff.getParentFile()); - } - return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey()); - } -} +/* + * 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 com.volmit.iris.core.commands; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.core.pregenerator.ChunkUpdater; +import com.volmit.iris.core.service.StudioSVC; +import com.volmit.iris.core.tools.IrisToolbelt; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.object.IrisDimension; +import com.volmit.iris.core.safeguard.UtilsSFG; +import com.volmit.iris.engine.object.IrisWorld; +import com.volmit.iris.engine.platform.BukkitChunkGenerator; +import com.volmit.iris.engine.platform.DummyChunkGenerator; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.decree.DecreeExecutor; +import com.volmit.iris.util.decree.DecreeOrigin; +import com.volmit.iris.util.decree.annotations.Decree; +import com.volmit.iris.util.decree.annotations.Param; +import com.volmit.iris.util.decree.specialhandlers.NullablePlayerHandler; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.plugin.VolmitSender; +import com.volmit.iris.util.scheduling.J; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.scheduler.BukkitRunnable; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import static com.volmit.iris.Iris.service; +import static com.volmit.iris.core.service.EditSVC.deletingWorld; +import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode; +import static com.volmit.iris.core.safeguard.ServerBootSFG.incompatibilities; +import static org.bukkit.Bukkit.getServer; + +@Decree(name = "iris", aliases = {"ir", "irs"}, description = "Basic Command") +public class CommandIris implements DecreeExecutor { + private CommandStudio studio; + private CommandPregen pregen; + private CommandSettings settings; + private CommandObject object; + private CommandJigsaw jigsaw; + private CommandWhat what; + private CommandEdit edit; + private CommandFind find; + private CommandDeveloper developer; + public static boolean worldCreation = false; + String WorldEngine; + String worldNameToCheck = "YourWorldName"; + VolmitSender sender = Iris.getSender(); + + @Decree(description = "Create a new world", aliases = {"+", "c"}) + public void create( + @Param(aliases = "world-name", description = "The name of the world to create") + String name, + @Param(aliases = "dimension", description = "The dimension type to create the world with", defaultValue = "default") + IrisDimension type, + @Param(description = "The seed to generate the world with", defaultValue = "1337") + long seed + ) { + if (name.equalsIgnoreCase("iris")) { + sender().sendMessage(C.RED + "You cannot use the world name \"iris\" for creating worlds as Iris uses this directory for studio worlds."); + sender().sendMessage(C.RED + "May we suggest the name \"IrisWorld\" instead?"); + return; + } + + if (name.equalsIgnoreCase("benchmark")) { + sender().sendMessage(C.RED + "You cannot use the world name \"benchmark\" for creating worlds as Iris uses this directory for Benchmarking Packs."); + sender().sendMessage(C.RED + "May we suggest the name \"IrisWorld\" instead?"); + return; + } + + if (new File(Bukkit.getWorldContainer(), name).exists()) { + sender().sendMessage(C.RED + "That folder already exists!"); + return; + } + + try { + worldCreation = true; + IrisToolbelt.createWorld() + .dimension(type.getLoadKey()) + .name(name) + .seed(seed) + .sender(sender()) + .studio(false) + .create(); + } catch (Throwable e) { + sender().sendMessage(C.RED + "Exception raised during creation. See the console for more details."); + Iris.error("Exception raised during world creation: " + e.getMessage()); + Iris.reportError(e); + worldCreation = false; + return; + } + worldCreation = false; + sender().sendMessage(C.GREEN + "Successfully created your world!"); + } + + @Decree(description = "Teleport to another world", aliases = {"tp"}, sync = true) + public void teleport( + @Param(description = "World to teleport to") + World world, + @Param(description = "Player to teleport", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + if (player == null && sender().isPlayer()) + player = sender().player(); + + final Player target = player; + if (target == null) { + sender().sendMessage(C.RED + "The specified player does not exist."); + return; + } + + new BukkitRunnable() { + @Override + public void run() { + target.teleport(world.getSpawnLocation()); + new VolmitSender(target).sendMessage(C.GREEN + "You have been teleported to " + world.getName() + "."); + } + }.runTask(Iris.instance); + } + + @Decree(description = "Print version information") + public void version() { + sender().sendMessage(C.GREEN + "Iris v" + Iris.instance.getDescription().getVersion() + " by Volmit Software"); + } + + /* + /todo + @Decree(description = "Benchmark a pack", origin = DecreeOrigin.CONSOLE) + public void packbenchmark( + @Param(description = "Dimension to benchmark") + IrisDimension type + ) throws InterruptedException { + + BenchDimension = type.getLoadKey(); + + IrisPackBenchmarking.runBenchmark(); + } */ + + @Decree(description = "Print world height information", origin = DecreeOrigin.PLAYER) + public void height() { + if (sender().isPlayer()) { + sender().sendMessage(C.GREEN + "" + sender().player().getWorld().getMinHeight() + " to " + sender().player().getWorld().getMaxHeight()); + sender().sendMessage(C.GREEN + "Total Height: " + (sender().player().getWorld().getMaxHeight() - sender().player().getWorld().getMinHeight())); + } else { + World mainWorld = getServer().getWorlds().get(0); + Iris.info(C.GREEN + "" + mainWorld.getMinHeight() + " to " + mainWorld.getMaxHeight()); + Iris.info(C.GREEN + "Total Height: " + (mainWorld.getMaxHeight() - mainWorld.getMinHeight())); + } + } + + @Decree(description = "QOL command to open a overworld studio world.", sync = true) + public void so() { + sender().sendMessage(C.GREEN + "Opening studio for the \"Overworld\" pack (seed: 1337)"); + Iris.service(StudioSVC.class).open(sender(), 1337, "overworld"); + } + + @Decree(description = "Check access of all worlds.", aliases = {"accesslist"}) + public void worlds() { + KList IrisWorlds = new KList<>(); + KList BukkitWorlds = new KList<>(); + + for (World w : Bukkit.getServer().getWorlds()) { + try { + Engine engine = IrisToolbelt.access(w).getEngine(); + if (engine != null) { + IrisWorlds.add(w); + } + } catch (Exception e) { + BukkitWorlds.add(w); + } + } + + if (sender().isPlayer()) { + sender().sendMessage(C.BLUE + "Iris Worlds: "); + for (World IrisWorld : IrisWorlds.copy()) { + sender().sendMessage(C.IRIS + "- " +IrisWorld.getName()); + } + sender().sendMessage(C.GOLD + "Bukkit Worlds: "); + for (World BukkitWorld : BukkitWorlds.copy()) { + sender().sendMessage(C.GRAY + "- " +BukkitWorld.getName()); + } + } else { + Iris.info(C.BLUE + "Iris Worlds: "); + for (World IrisWorld : IrisWorlds.copy()) { + Iris.info(C.IRIS + "- " +IrisWorld.getName()); + } + Iris.info(C.GOLD + "Bukkit Worlds: "); + for (World BukkitWorld : BukkitWorlds.copy()) { + Iris.info(C.GRAY + "- " +BukkitWorld.getName()); + } + + } + } + + @Decree(description = "Remove an Iris world", aliases = {"del", "rm", "delete"}, sync = true) + public void remove( + @Param(description = "The world to remove") + World world, + @Param(description = "Whether to also remove the folder (if set to false, just does not load the world)", defaultValue = "true") + boolean delete + ) { + if (!IrisToolbelt.isIrisWorld(world)) { + sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); + return; + } + sender().sendMessage(C.GREEN + "Removing world: " + world.getName()); + + if (!IrisToolbelt.evacuate(world)) { + sender().sendMessage(C.RED + "Failed to evacuate world: " + world.getName()); + return; + } + + if (!Bukkit.unloadWorld(world, false)) { + sender().sendMessage(C.RED + "Failed to unload world: " + world.getName()); + return; + } + + try { + if (IrisToolbelt.removeWorld(world)) { + sender().sendMessage(C.GREEN + "Successfully removed " + world.getName() + " from bukkit.yml"); + } else { + sender().sendMessage(C.YELLOW + "Looks like the world was already removed from bukkit.yml"); + } + } catch (IOException e) { + sender().sendMessage(C.RED + "Failed to save bukkit.yml because of " + e.getMessage()); + e.printStackTrace(); + } + IrisToolbelt.evacuate(world, "Deleting world"); + deletingWorld = true; + if (!delete) { + deletingWorld = false; + return; + } + VolmitSender sender = sender(); + J.a(() -> { + int retries = 12; + + if (deleteDirectory(world.getWorldFolder())) { + sender.sendMessage(C.GREEN + "Successfully removed world folder"); + } else { + while(true){ + if (deleteDirectory(world.getWorldFolder())){ + sender.sendMessage(C.GREEN + "Successfully removed world folder"); + break; + } + retries--; + if (retries == 0){ + sender.sendMessage(C.RED + "Failed to remove world folder"); + break; + } + J.sleep(3000); + } + } + deletingWorld = false; + }); + } + + public static boolean deleteDirectory(File dir) { + if (dir.isDirectory()) { + File[] children = dir.listFiles(); + for (int i = 0; i < children.length; i++) { + boolean success = deleteDirectory(children[i]); + if (!success) { + return false; + } + } + } + return dir.delete(); + } + + @Decree(description = "Updates all chunk in the specified world") + public void updater( + @Param(description = "World to update chunks at") + World world + ) { + if (!IrisToolbelt.isIrisWorld(world)) { + sender().sendMessage(C.GOLD + "This is not an Iris world"); + return; + } + ChunkUpdater updater = new ChunkUpdater(world); + if (sender().isPlayer()) { + sender().sendMessage(C.GREEN + "Updating " + world.getName() + " Total chunks: " + Form.f(updater.getChunks())); + } else { + Iris.info(C.GREEN + "Updating " + world.getName() + " Total chunks: " + Form.f(updater.getChunks())); + } + updater.start(); + } + + @Decree(description = "Set aura spins") + public void aura( + @Param(description = "The h color value", defaultValue = "-20") + int h, + @Param(description = "The s color value", defaultValue = "7") + int s, + @Param(description = "The b color value", defaultValue = "8") + int b + ) { + IrisSettings.get().getGeneral().setSpinh(h); + IrisSettings.get().getGeneral().setSpins(s); + IrisSettings.get().getGeneral().setSpinb(b); + IrisSettings.get().forceSave(); + sender().sendMessage("Aura Spins updated to " + h + " " + s + " " + b); + } + + @Decree(description = "Bitwise calculations") + public void bitwise( + @Param(description = "The first value to run calculations on") + int value1, + @Param(description = "The operator: | & ^ ≺≺ ≻≻ %") + String operator, + @Param(description = "The second value to run calculations on") + int value2 + ) { + Integer v = null; + switch (operator) { + case "|" -> v = value1 | value2; + case "&" -> v = value1 & value2; + case "^" -> v = value1 ^ value2; + case "%" -> v = value1 % value2; + case ">>" -> v = value1 >> value2; + case "<<" -> v = value1 << value2; + } + if (v == null) { + sender().sendMessage(C.RED + "The operator you entered: (" + operator + ") is invalid!"); + return; + } + sender().sendMessage(C.GREEN + "" + value1 + " " + C.GREEN + operator.replaceAll("<", "≺").replaceAll(">", "≻").replaceAll("%", "%") + " " + C.GREEN + value2 + C.GREEN + " returns " + C.GREEN + v); + } + + @Decree(description = "Toggle debug") + public void debug( + @Param(name = "on", description = "Whether or not debug should be on", defaultValue = "other") + Boolean on + ) { + boolean to = on == null ? !IrisSettings.get().getGeneral().isDebug() : on; + IrisSettings.get().getGeneral().setDebug(to); + IrisSettings.get().forceSave(); + sender().sendMessage(C.GREEN + "Set debug to: " + to); + } + + @Decree(description = "Download a project.", aliases = "dl") + public void download( + @Param(name = "pack", description = "The pack to download", defaultValue = "overworld", aliases = "project") + String pack, + @Param(name = "branch", description = "The branch to download from", defaultValue = "main") + String branch, + @Param(name = "trim", description = "Whether or not to download a trimmed version (do not enable when editing)", defaultValue = "false") + boolean trim, + @Param(name = "overwrite", description = "Whether or not to overwrite the pack with the downloaded one", aliases = "force", defaultValue = "false") + boolean overwrite + ) { + sender().sendMessage(C.GREEN + "Downloading pack: " + pack + "/" + branch + (trim ? " trimmed" : "") + (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, trim, overwrite); + } else { + Iris.service(StudioSVC.class).downloadSearch(sender(), "IrisDimensions/" + pack + "/" + branch, trim, overwrite); + } + } + + @Decree(description = "Get metrics for your world", aliases = "measure", origin = DecreeOrigin.PLAYER) + public void metrics() { + if (!IrisToolbelt.isIrisWorld(world())) { + sender().sendMessage(C.RED + "You must be in an Iris world"); + return; + } + sender().sendMessage(C.GREEN + "Sending metrics..."); + engine().printMetrics(sender()); + } + + @Decree(description = "Reload configuration file (this is also done automatically)") + public void reload() { + IrisSettings.invalidate(); + IrisSettings.get(); + sender().sendMessage(C.GREEN + "Hotloaded settings"); + } + + @Decree(description = "Update the pack of a world (UNSAFE!)", name = "^world", aliases = "update-world") + public void updateWorld( + @Param(description = "The world to update", contextual = true) + World world, + @Param(description = "The pack to install into the world", contextual = true, aliases = "dimension") + IrisDimension pack, + @Param(description = "Make sure to make a backup & read the warnings first!", defaultValue = "false", aliases = "c") + boolean confirm, + @Param(description = "Should Iris download the pack again for you", defaultValue = "false", name = "fresh-download", aliases = {"fresh", "new"}) + boolean freshDownload + ) { + if (!confirm) { + sender().sendMessage(new String[]{ + C.RED + "You should always make a backup before using this", + C.YELLOW + "Issues caused by this can be, but are not limited to:", + C.YELLOW + " - Broken chunks (cut-offs) between old and new chunks (before & after the update)", + C.YELLOW + " - Regenerated chunks that do not fit in with the old chunks", + C.YELLOW + " - Structures not spawning again when regenerating", + C.YELLOW + " - Caves not lining up", + C.YELLOW + " - Terrain layers not lining up", + C.RED + "Now that you are aware of the risks, and have made a back-up:", + C.RED + "/iris ^world " + world.getName() + " " + pack.getLoadKey() + " confirm=true" + }); + return; + } + + File folder = world.getWorldFolder(); + folder.mkdirs(); + + if (freshDownload) { + Iris.service(StudioSVC.class).downloadSearch(sender(), pack.getLoadKey(), false, true); + } + + Iris.service(StudioSVC.class).installIntoWorld(sender(), pack.getLoadKey(), folder); + } + + @Decree(description = "Unload an Iris World", origin = DecreeOrigin.PLAYER, sync = true) + public void unloadWorld( + @Param(description = "The world to unload") + World world + ) { + if (!IrisToolbelt.isIrisWorld(world)) { + sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); + return; + } + sender().sendMessage(C.GREEN + "Unloading world: " + world.getName()); + try { + IrisToolbelt.evacuate(world); + Bukkit.unloadWorld(world, false); + sender().sendMessage(C.GREEN + "World unloaded successfully."); + } catch (Exception e) { + sender().sendMessage(C.RED + "Failed to unload the world: " + e.getMessage()); + e.printStackTrace(); + } + } + + @Decree(description = "Load an Iris World", origin = DecreeOrigin.PLAYER, sync = true, aliases = {"import"}) + public void loadWorld( + @Param(description = "The name of the world to load") + String world + ) { + World worldloaded = Bukkit.getWorld(world); + worldNameToCheck = world; + boolean worldExists = doesWorldExist(worldNameToCheck); + WorldEngine = world; + + if (!worldExists) { + sender().sendMessage(C.YELLOW + world + " Doesnt exist on the server."); + return; + } + + File BUKKIT_YML = new File("bukkit.yml"); + String pathtodim = world + File.separator +"iris"+File.separator +"pack"+File.separator +"dimensions"+File.separator; + File directory = new File(Bukkit.getWorldContainer(), pathtodim); + + String dimension = null; + if (directory.exists() && directory.isDirectory()) { + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isFile()) { + String fileName = file.getName(); + if (fileName.endsWith(".json")) { + dimension = fileName.substring(0, fileName.length() - 5); + sender().sendMessage(C.BLUE + "Generator: " + dimension); + } + } + } + } + } else { + sender().sendMessage(C.GOLD + world + " is not an iris world."); + return; + } + sender().sendMessage(C.GREEN + "Loading world: " + world); + + YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML); + String gen = "Iris:" + dimension; + ConfigurationSection section = yml.contains("worlds") ? yml.getConfigurationSection("worlds") : yml.createSection("worlds"); + if (!section.contains(world)) { + section.createSection(world).set("generator", gen); + try { + yml.save(BUKKIT_YML); + Iris.info("Registered \"" + world + "\" in bukkit.yml"); + } catch (IOException e) { + Iris.error("Failed to update bukkit.yml!"); + e.printStackTrace(); + return; + } + } + checkForBukkitWorlds(world); + sender().sendMessage(C.GREEN + world + " loaded successfully."); + } + @Decree(description = "Evacuate an iris world", origin = DecreeOrigin.PLAYER, sync = true) + public void evacuate( + @Param(description = "Evacuate the world") + World world + ) { + if (!IrisToolbelt.isIrisWorld(world)) { + sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); + return; + } + sender().sendMessage(C.GREEN + "Evacuating world" + world.getName()); + IrisToolbelt.evacuate(world); + } + + boolean doesWorldExist(String worldName) { + File worldContainer = Bukkit.getWorldContainer(); + File worldDirectory = new File(worldContainer, worldName); + return worldDirectory.exists() && worldDirectory.isDirectory(); + } + private void checkForBukkitWorlds(String world) { + FileConfiguration fc = new YamlConfiguration(); + try { + fc.load(new File("bukkit.yml")); + ConfigurationSection section = fc.getConfigurationSection("worlds"); + if (section == null) { + return; + } + + List worldsToLoad = Collections.singletonList(world); + + for (String s : section.getKeys(false)) { + if (!worldsToLoad.contains(s)) { + continue; + } + ConfigurationSection entry = section.getConfigurationSection(s); + if (!entry.contains("generator", true)) { + continue; + } + String generator = entry.getString("generator"); + if (generator.startsWith("Iris:")) { + generator = generator.split("\\Q:\\E")[1]; + } else if (generator.equalsIgnoreCase("Iris")) { + generator = IrisSettings.get().getGenerator().getDefaultWorldType(); + } else { + continue; + } + Iris.info("2 World: %s | Generator: %s", s, generator); + if (Bukkit.getWorlds().stream().anyMatch(w -> w.getName().equals(s))) { + continue; + } + Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "..."); + WorldCreator c = new WorldCreator(s) + .generator(getDefaultWorldGenerator(s, generator)) + .environment(IrisData.loadAnyDimension(generator).getEnvironment()); + INMS.get().createWorld(c); + Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!"); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { + Iris.debug("Default World Generator Called for " + worldName + " using ID: " + id); + if (worldName.equals("test")) { + try { + throw new RuntimeException(); + } catch (Throwable e) { + Iris.info(e.getStackTrace()[1].getClassName()); + if (e.getStackTrace()[1].getClassName().contains("com.onarandombox.MultiverseCore")) { + Iris.debug("MVC Test detected, Quick! Send them the dummy!"); + return new DummyChunkGenerator(); + } + } + } + IrisDimension dim; + if (id == null || id.isEmpty()) { + dim = IrisData.loadAnyDimension(IrisSettings.get().getGenerator().getDefaultWorldType()); + } else { + dim = IrisData.loadAnyDimension(id); + } + Iris.debug("Generator ID: " + id + " requested by bukkit/plugin"); + + if (dim == null) { + Iris.warn("Unable to find dimension type " + id + " Looking for online packs..."); + + service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, true); + dim = IrisData.loadAnyDimension(id); + + if (dim == null) { + throw new RuntimeException("Can't find dimension " + id + "!"); + } else { + Iris.info("Resolved missing dimension, proceeding with generation."); + } + } + Iris.debug("Assuming IrisDimension: " + dim.getName()); + IrisWorld w = IrisWorld.builder() + .name(worldName) + .seed(1337) + .environment(dim.getEnvironment()) + .worldFolder(new File(Bukkit.getWorldContainer(), worldName)) + .minHeight(dim.getMinHeight()) + .maxHeight(dim.getMaxHeight()) + .build(); + Iris.debug("Generator Config: " + w.toString()); + File ff = new File(w.worldFolder(), "iris/pack"); + if (!ff.exists() || ff.listFiles().length == 0) { + ff.mkdirs(); + service(StudioSVC.class).installIntoWorld(sender, dim.getLoadKey(), ff.getParentFile()); + } + return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey()); + } +} From d49f7d7821733751989d12e269367ab285f27dbf Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 13 Jul 2025 17:43:11 +0200 Subject: [PATCH 135/138] add parameter to the create command to make it your main world --- .../iris/core/commands/CommandIris.java | 50 ++++++++++++------- .../main/java/com/volmit/iris/util/io/IO.java | 20 ++++++++ 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java index 38e2266e3..84f601642 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java @@ -27,10 +27,8 @@ import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.IrisDimension; -import com.volmit.iris.core.safeguard.UtilsSFG; import com.volmit.iris.engine.object.IrisWorld; import com.volmit.iris.engine.platform.BukkitChunkGenerator; -import com.volmit.iris.engine.platform.DummyChunkGenerator; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.decree.DecreeExecutor; import com.volmit.iris.util.decree.DecreeOrigin; @@ -39,8 +37,11 @@ import com.volmit.iris.util.decree.annotations.Param; import com.volmit.iris.util.decree.specialhandlers.NullablePlayerHandler; import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.io.IO; +import com.volmit.iris.util.misc.ServerProperties; import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.scheduling.J; +import lombok.SneakyThrows; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.WorldCreator; @@ -51,15 +52,13 @@ import org.bukkit.entity.Player; import org.bukkit.generator.ChunkGenerator; import org.bukkit.scheduler.BukkitRunnable; -import java.io.File; -import java.io.IOException; +import java.io.*; import java.util.Collections; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import static com.volmit.iris.Iris.service; import static com.volmit.iris.core.service.EditSVC.deletingWorld; -import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode; -import static com.volmit.iris.core.safeguard.ServerBootSFG.incompatibilities; import static org.bukkit.Bukkit.getServer; @Decree(name = "iris", aliases = {"ir", "irs"}, description = "Basic Command") @@ -74,6 +73,7 @@ public class CommandIris implements DecreeExecutor { private CommandFind find; private CommandDeveloper developer; public static boolean worldCreation = false; + private static final AtomicReference mainWorld = new AtomicReference<>(); String WorldEngine; String worldNameToCheck = "YourWorldName"; VolmitSender sender = Iris.getSender(); @@ -85,7 +85,9 @@ public class CommandIris implements DecreeExecutor { @Param(aliases = "dimension", description = "The dimension type to create the world with", defaultValue = "default") IrisDimension type, @Param(description = "The seed to generate the world with", defaultValue = "1337") - long seed + long seed, + @Param(aliases = "main-world", description = "Whether or not to automatically use this world as the main world", defaultValue = "false") + boolean main ) { if (name.equalsIgnoreCase("iris")) { sender().sendMessage(C.RED + "You cannot use the world name \"iris\" for creating worlds as Iris uses this directory for studio worlds."); @@ -113,6 +115,12 @@ public class CommandIris implements DecreeExecutor { .sender(sender()) .studio(false) .create(); + if (main) { + Runtime.getRuntime().addShutdownHook(mainWorld.updateAndGet(old -> { + if (old != null) Runtime.getRuntime().removeShutdownHook(old); + return new Thread(() -> updateMainWorld(name)); + })); + } } catch (Throwable e) { sender().sendMessage(C.RED + "Exception raised during creation. See the console for more details."); Iris.error("Exception raised during world creation: " + e.getMessage()); @@ -124,6 +132,23 @@ public class CommandIris implements DecreeExecutor { sender().sendMessage(C.GREEN + "Successfully created your world!"); } + @SneakyThrows + private void updateMainWorld(String newName) { + File worlds = Bukkit.getWorldContainer(); + var data = ServerProperties.DATA; + try (var in = new FileInputStream(ServerProperties.SERVER_PROPERTIES)) { + data.load(in); + } + for (String sub : List.of("datapacks", "playerdata", "advancements", "stats")) { + IO.copyDirectory(new File(worlds, ServerProperties.LEVEL_NAME + "/" + sub).toPath(), new File(worlds, newName + "/" + sub).toPath()); + } + + data.setProperty("level-name", newName); + try (var out = new FileOutputStream(ServerProperties.SERVER_PROPERTIES)) { + data.store(out, null); + } + } + @Decree(description = "Teleport to another world", aliases = {"tp"}, sync = true) public void teleport( @Param(description = "World to teleport to") @@ -580,17 +605,6 @@ public class CommandIris implements DecreeExecutor { } public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { Iris.debug("Default World Generator Called for " + worldName + " using ID: " + id); - if (worldName.equals("test")) { - try { - throw new RuntimeException(); - } catch (Throwable e) { - Iris.info(e.getStackTrace()[1].getClassName()); - if (e.getStackTrace()[1].getClassName().contains("com.onarandombox.MultiverseCore")) { - Iris.debug("MVC Test detected, Quick! Send them the dummy!"); - return new DummyChunkGenerator(); - } - } - } IrisDimension dim; if (id == null || id.isEmpty()) { dim = IrisData.loadAnyDimension(IrisSettings.get().getGenerator().getDefaultWorldType()); diff --git a/core/src/main/java/com/volmit/iris/util/io/IO.java b/core/src/main/java/com/volmit/iris/util/io/IO.java index 61deb3670..247dfefb1 100644 --- a/core/src/main/java/com/volmit/iris/util/io/IO.java +++ b/core/src/main/java/com/volmit/iris/util/io/IO.java @@ -30,6 +30,7 @@ import org.apache.commons.io.function.IOFunction; import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.security.DigestInputStream; import java.security.MessageDigest; @@ -592,6 +593,25 @@ public class IO { } } + public static void copyDirectory(Path source, Path target) throws IOException { + Files.walk(source).forEach(sourcePath -> { + Path targetPath = target.resolve(source.relativize(sourcePath)); + + try { + if (Files.isDirectory(sourcePath)) { + if (!Files.exists(targetPath)) { + Files.createDirectories(targetPath); + } + } else { + Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); + } + } catch (IOException e) { + Iris.error("Failed to copy " + targetPath); + e.printStackTrace(); + } + }); + } + /** * Unconditionally close an Reader. *

From a8892b04ef6074096d2500e5f2079f8ade2b4c52 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 19 Jul 2025 12:59:50 +0200 Subject: [PATCH 136/138] bump nms for 1.21.8 support --- .../java/com/volmit/iris/core/nms/INMS.java | 37 ++++++++++--------- gradle/libs.versions.toml | 2 +- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMS.java b/core/src/main/java/com/volmit/iris/core/nms/INMS.java index 8b998147c..e4d6bc682 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMS.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMS.java @@ -24,21 +24,21 @@ import com.volmit.iris.core.nms.v1X.NMSBinding1X; import org.bukkit.Bukkit; import java.util.List; -import java.util.Map; public class INMS { - private static final Map REVISION = Map.of( - "1.20.5", "v1_20_R4", - "1.20.6", "v1_20_R4", - "1.21", "v1_21_R1", - "1.21.1", "v1_21_R1", - "1.21.2", "v1_21_R2", - "1.21.3", "v1_21_R2", - "1.21.4", "v1_21_R3", - "1.21.5", "v1_21_R4", - "1.21.6", "v1_21_R5", - "1.21.7", "v1_21_R5" + private static final Version CURRENT = Boolean.getBoolean("iris.no-version-limit") ? + new Version(Integer.MAX_VALUE, Integer.MAX_VALUE, null) : + new Version(21, 8, null); + + private static final List REVISION = List.of( + new Version(21, 6, "v1_21_R5"), + new Version(21, 5, "v1_21_R4"), + new Version(21, 4, "v1_21_R3"), + new Version(21, 2, "v1_21_R2"), + new Version(21, 0, "v1_21_R1"), + new Version(20, 5, "v1_20_R4") ); + private static final List PACKS = List.of( new Version(21, 5, "31100"), new Version(21, 4, "31020"), @@ -48,7 +48,7 @@ public class INMS { //@done private static final INMSBinding binding = bind(); - public static final String OVERWORLD_TAG = getOverworldTag(); + public static final String OVERWORLD_TAG = getTag(PACKS, "3910"); public static INMSBinding get() { return binding; @@ -62,7 +62,7 @@ public class INMS { try { String name = Bukkit.getServer().getClass().getCanonicalName(); if (name.equals("org.bukkit.craftbukkit.CraftServer")) { - return REVISION.getOrDefault(Bukkit.getServer().getBukkitVersion().split("-")[0], "BUKKIT"); + return getTag(REVISION, "BUKKIT"); } else { return name.split("\\Q.\\E")[3]; } @@ -100,7 +100,7 @@ public class INMS { return new NMSBinding1X(); } - private static String getOverworldTag() { + private static String getTag(List versions, String def) { var version = Bukkit.getServer().getBukkitVersion().split("-")[0].split("\\.", 3); int major = 0; int minor = 0; @@ -111,13 +111,16 @@ public class INMS { } else if (version.length == 2) { major = Integer.parseInt(version[1]); } + if (CURRENT.major < major || CURRENT.minor < minor) { + return versions.getFirst().tag; + } - for (var p : PACKS) { + for (var p : versions) { if (p.major > major || p.minor > minor) continue; return p.tag; } - return "3910"; + return def; } private record Version(int major, int minor, String tag) {} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bdc075162..f697c836e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ [versions] # Plugins shadow = "9.0.0-rc1" # https://plugins.gradle.org/plugin/com.gradleup.shadow -slimjar = "2.0.7" # https://plugins.gradle.org/plugin/de.crazydev22.slimjar +slimjar = "2.0.8" # https://plugins.gradle.org/plugin/de.crazydev22.slimjar download = "5.6.0" # https://plugins.gradle.org/plugin/de.undercouch.download runPaper = "2.3.1" # https://plugins.gradle.org/plugin/xyz.jpenilla.run-paper sentryPlugin = "5.8.0" # https://github.com/getsentry/sentry-android-gradle-plugin From dee46a42843b6a8965e1cc7e631ee4172c2cc0e7 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 19 Jul 2025 17:48:58 +0200 Subject: [PATCH 137/138] cleanup object mask rotation --- .../iris/engine/object/IrisCaveShape.java | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCaveShape.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCaveShape.java index f5001befb..4972ce516 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCaveShape.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCaveShape.java @@ -6,6 +6,7 @@ import com.volmit.iris.engine.object.annotations.RegistryListResource; import com.volmit.iris.engine.object.annotations.Snippet; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.noise.CNG; import lombok.AllArgsConstructor; @@ -36,17 +37,40 @@ public class IrisCaveShape { public KSet getMasked(RNG rng, Engine engine) { if (object == null) return null; - return cache.computeIfAbsent(new IrisPosition( - rng.i(0, 360), - rng.i(0, 360), - rng.i(0, 360)), - pos -> { - var rotated = new KSet(); - engine.getData().getObjectLoader().load(object).getBlocks().forEach((vector, data) -> { - if (data.getMaterial().isAir()) return; - rotated.add(new IrisPosition(objectRotation.rotate(vector, pos.getX(), pos.getY(), pos.getZ()))); - }); - return rotated; - }); + return cache.computeIfAbsent(randomRotation(rng), pos -> { + var rotated = new KSet(); + engine.getData().getObjectLoader().load(object).getBlocks().forEach((vector, data) -> { + if (data.getMaterial().isAir()) return; + rotated.add(new IrisPosition(objectRotation.rotate(vector, pos.getX(), pos.getY(), pos.getZ()))); + }); + return rotated; + }); + } + + private IrisPosition randomRotation(RNG rng) { + if (objectRotation == null || !objectRotation.canRotate()) + return new IrisPosition(0,0,0); + return new IrisPosition( + randomDegree(rng, objectRotation.getXAxis()), + randomDegree(rng, objectRotation.getYAxis()), + randomDegree(rng, objectRotation.getZAxis()) + ); + } + + private int randomDegree(RNG rng, IrisAxisRotationClamp clamp) { + if (!clamp.isEnabled()) return 0; + if (clamp.isLocked()) return (int) clamp.getMax(); + double interval = clamp.getInterval(); + if (interval < 1) interval = 1; + + double min = clamp.getMin(), max = clamp.getMax(); + double value = (interval * (Math.ceil(Math.abs(rng.d(0, 360) / interval)))) % 360D; + if (clamp.isUnlimited()) return (int) value; + + if (min > max) { + max = clamp.getMin(); + min = clamp.getMax(); + } + return (int) (double) M.clip(value, min, max); } } From 8262e52893e216757cde2af313bcf8be2ef47248 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 30 Mar 2025 13:48:43 +0200 Subject: [PATCH 138/138] cleanup regen --- .../iris/core/commands/CommandStudio.java | 140 +++++++++--------- .../engine/platform/BukkitChunkGenerator.java | 74 ++++----- .../platform/PlatformChunkGenerator.java | 4 +- .../iris/util/parallel/SyncExecutor.java | 46 ++++++ 4 files changed, 160 insertions(+), 104 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/util/parallel/SyncExecutor.java diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java index 0abb77a15..1635dd3bb 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java @@ -46,18 +46,20 @@ import com.volmit.iris.util.interpolation.InterpolationMethod; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.json.JSONArray; import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.mantle.MantleChunk; +import com.volmit.iris.util.mantle.MantleFlag; import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.Spiraler; import com.volmit.iris.util.noise.CNG; -import com.volmit.iris.util.parallel.BurstExecutor; import com.volmit.iris.util.parallel.MultiBurst; +import com.volmit.iris.util.parallel.SyncExecutor; import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.O; import com.volmit.iris.util.scheduling.PrecisionStopwatch; -import com.volmit.iris.util.scheduling.jobs.QueueJob; +import com.volmit.iris.util.scheduling.jobs.ParallelQueueJob; import io.papermc.lib.PaperLib; import org.bukkit.*; import org.bukkit.event.inventory.InventoryType; @@ -76,8 +78,7 @@ import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Date; import java.util.Objects; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; @@ -161,70 +162,77 @@ public class CommandStudio implements DecreeExecutor { @Param(name = "radius", description = "The radius of nearby cunks", defaultValue = "5") int radius ) { - if (IrisToolbelt.isIrisWorld(player().getWorld())) { - VolmitSender sender = sender(); - J.a(() -> { - DecreeContext.touch(sender); - PlatformChunkGenerator plat = IrisToolbelt.access(player().getWorld()); - Engine engine = plat.getEngine(); - try { - Chunk cx = player().getLocation().getChunk(); - KList js = new KList<>(); - BurstExecutor b = MultiBurst.burst.burst(); - b.setMulticore(false); - int rad = engine.getMantle().getRadius(); - for (int i = -(radius + rad); i <= radius + rad; i++) { - for (int j = -(radius + rad); j <= radius + rad; j++) { - engine.getMantle().getMantle().deleteChunk(i + cx.getX(), j + cx.getZ()); - } - } - - for (int i = -radius; i <= radius; i++) { - for (int j = -radius; j <= radius; j++) { - int finalJ = j; - int finalI = i; - b.queue(() -> plat.injectChunkReplacement(player().getWorld(), finalI + cx.getX(), finalJ + cx.getZ(), (f) -> { - synchronized (js) { - js.add(f); - } - })); - } - } - - b.complete(); - sender().sendMessage(C.GREEN + "Regenerating " + Form.f(js.size()) + " Sections"); - QueueJob r = new QueueJob<>() { - final KList> futures = new KList<>(); - - @Override - public void execute(Runnable runnable) { - futures.add(J.sfut(runnable)); - - if (futures.size() > 64) { - while (futures.isNotEmpty()) { - try { - futures.remove(0).get(); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - } - } - } - } - - @Override - public String getName() { - return "Regenerating"; - } - }; - r.queue(js); - r.execute(sender()); - } catch (Throwable e) { - sender().sendMessage("Unable to parse view-distance"); - } - }); - } else { + World world = player().getWorld(); + if (!IrisToolbelt.isIrisWorld(world)) { sender().sendMessage(C.RED + "You must be in an Iris World to use regen!"); } + + VolmitSender sender = sender(); + var loc = player().getLocation().clone(); + + J.a(() -> { + DecreeContext.touch(sender); + PlatformChunkGenerator plat = IrisToolbelt.access(world); + Engine engine = plat.getEngine(); + try (SyncExecutor executor = new SyncExecutor(20)) { + int x = loc.getBlockX() >> 4; + int z = loc.getBlockZ() >> 4; + + int rad = engine.getMantle().getRadius(); + var mantle = engine.getMantle().getMantle(); + var chunkMap = new KMap(); + for (int i = -(radius + rad); i <= radius + rad; i++) { + for (int j = -(radius + rad); j <= radius + rad; j++) { + int xx = i + x, zz = j + z; + if (Math.abs(i) <= radius && Math.abs(j) <= radius) { + mantle.deleteChunk(xx, zz); + continue; + } + chunkMap.put(new Position2(xx, zz), mantle.getChunk(xx, zz)); + mantle.deleteChunk(xx, zz); + } + } + + ParallelQueueJob job = new ParallelQueueJob<>() { + @Override + public void execute(Position2 p) { + plat.injectChunkReplacement(world, p.getX(), p.getZ(), executor); + } + + @Override + public String getName() { + return "Regenerating"; + } + }; + for (int i = -radius; i <= radius; i++) { + for (int j = -radius; j <= radius; j++) { + job.queue(new Position2(i + x, j + z)); + } + } + + CountDownLatch latch = new CountDownLatch(1); + job.execute(sender(), latch::countDown); + latch.await(); + + int sections = mantle.getWorldHeight() >> 4; + chunkMap.forEach((pos, chunk) -> { + var c = mantle.getChunk(pos.getX(), pos.getZ()); + for (MantleFlag flag : MantleFlag.values()) { + c.flag(flag, chunk.isFlagged(flag)); + } + c.clear(); + for (int y = 0; y < sections; y++) { + var slice = chunk.get(y); + if (slice == null) continue; + var s = c.getOrCreate(y); + slice.getSliceMap().forEach(s::putSlice); + } + }); + } catch (Throwable e) { + sender().sendMessage("Error while regenerating chunks"); + e.printStackTrace(); + } + }); } @Decree(description = "Convert objects in the \"convert\" folder") diff --git a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index 2e6035534..1f7fbd9a6 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -64,12 +64,10 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.List; import java.util.Random; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Semaphore; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Consumer; @EqualsAndHashCode(callSuper = true) @Data @@ -95,8 +93,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun @Setter private volatile StudioGenerator studioGenerator; - private boolean initialized = false; - public BukkitChunkGenerator(IrisWorld world, boolean studio, File dataLocation, String dimensionKey) { setup = new AtomicBoolean(false); studioGenerator = null; @@ -114,8 +110,8 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun @EventHandler(priority = EventPriority.LOWEST) public void onWorldInit(WorldInitEvent event) { - if (initialized || !world.name().equals(event.getWorld().getName())) - return; + if (!world.name().equals(event.getWorld().getName())) return; + Iris.instance.unregisterListener(this); world.setRawWorldSeed(event.getWorld().getSeed()); if (initialize(event.getWorld())) return; @@ -140,7 +136,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun } spawnChunks.complete(INMS.get().getSpawnChunkCount(world)); Iris.instance.unregisterListener(this); - initialized = true; IrisWorlds.get().put(world.getName(), dimensionKey); return true; } @@ -205,50 +200,57 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun } @Override - public void injectChunkReplacement(World world, int x, int z, Consumer jobs) { + public void injectChunkReplacement(World world, int x, int z, Executor syncExecutor) { try { loadLock.acquire(); IrisBiomeStorage st = new IrisBiomeStorage(); TerrainChunk tc = TerrainChunk.createUnsafe(world, st); - Hunk blocks = Hunk.view(tc); - Hunk biomes = Hunk.view(tc, tc.getMinHeight(), tc.getMaxHeight()); this.world.bind(world); - getEngine().generate(x << 4, z << 4, blocks, biomes, true); - Iris.debug("Regenerated " + x + " " + z); - int t = 0; + getEngine().generate(x << 4, z << 4, tc, false); + + Chunk c = PaperLib.getChunkAtAsync(world, x, z) + .thenApply(d -> { + d.addPluginChunkTicket(Iris.instance); + + for (Entity ee : d.getEntities()) { + if (ee instanceof Player) { + continue; + } + + ee.remove(); + } + + engine.getWorldManager().onChunkLoad(d, false); + return d; + }).get(); + + + KList> futures = new KList<>(1 + getEngine().getHeight() >> 4); for (int i = getEngine().getHeight() >> 4; i >= 0; i--) { - if (!world.isChunkLoaded(x, z)) { - continue; - } - - Chunk c = world.getChunkAt(x, z); - for (Entity ee : c.getEntities()) { - if (ee instanceof Player) { - continue; - } - - J.s(ee::remove); - } - - J.s(() -> engine.getWorldManager().onChunkLoad(c, false)); - - int finalI = i; - jobs.accept(() -> { - + int finalI = i << 4; + futures.add(CompletableFuture.runAsync(() -> { for (int xx = 0; xx < 16; xx++) { for (int yy = 0; yy < 16; yy++) { for (int zz = 0; zz < 16; zz++) { - if (yy + (finalI << 4) >= engine.getHeight() || yy + (finalI << 4) < 0) { + if (yy + finalI >= engine.getHeight() || yy + finalI < 0) { continue; } - c.getBlock(xx, yy + (finalI << 4) + world.getMinHeight(), zz) - .setBlockData(tc.getBlockData(xx, yy + (finalI << 4) + world.getMinHeight(), zz), false); + int y = yy + finalI + world.getMinHeight(); + c.getBlock(xx, y, zz).setBlockData(tc.getBlockData(xx, y, zz), false); } } } - }); + }, syncExecutor)); } + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) + .thenRunAsync(() -> { + c.removePluginChunkTicket(Iris.instance); + c.unload(); + }, syncExecutor) + .get(); + Iris.debug("Regenerated " + x + " " + z); + loadLock.release(); } catch (Throwable e) { loadLock.release(); diff --git a/core/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java index 687788527..93066fb62 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java @@ -28,7 +28,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; +import java.util.concurrent.Executor; public interface PlatformChunkGenerator extends Hotloadable, DataProvider { @Nullable @@ -42,7 +42,7 @@ public interface PlatformChunkGenerator extends Hotloadable, DataProvider { @NotNull EngineTarget getTarget(); - void injectChunkReplacement(World world, int x, int z, Consumer jobs); + void injectChunkReplacement(World world, int x, int z, Executor syncExecutor); void close(); diff --git a/core/src/main/java/com/volmit/iris/util/parallel/SyncExecutor.java b/core/src/main/java/com/volmit/iris/util/parallel/SyncExecutor.java new file mode 100644 index 000000000..00966b226 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/parallel/SyncExecutor.java @@ -0,0 +1,46 @@ +package com.volmit.iris.util.parallel; + +import com.volmit.iris.util.math.M; +import com.volmit.iris.util.scheduling.SR; +import org.jetbrains.annotations.NotNull; + +import java.util.Queue; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; + +public class SyncExecutor implements Executor, AutoCloseable { + private final CountDownLatch latch = new CountDownLatch(1); + private final Queue queue = new ConcurrentLinkedQueue<>(); + private final AtomicBoolean closed = new AtomicBoolean(false); + + public SyncExecutor(int msPerTick) { + new SR() { + @Override + public void run() { + var time = M.ms() + msPerTick; + while (time > M.ms()) { + Runnable r = queue.poll(); + if (r == null) break; + r.run(); + } + + if (closed.get() && queue.isEmpty()) { + cancel(); + latch.countDown(); + } + } + }; + } + + @Override + public void execute(@NotNull Runnable command) { + if (closed.get()) throw new IllegalStateException("Executor is closed!"); + queue.add(command); + } + + @Override + public void close() throws Exception { + closed.set(true); + latch.await(); + } +}