From 74e2576ca2761db11f8479de4afc22bb381165b5 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 6 Apr 2025 16:31:35 +0200 Subject: [PATCH 01/12] 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 71078a20a9917f080bba15cc1a3648e42246095b Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 8 Apr 2025 21:15:19 +0200 Subject: [PATCH 02/12] 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 03/12] 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 04/12] 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 05/12] 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 3e2c0fa02512779865b70892a3bf1b5df6d78043 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Mon, 31 Mar 2025 16:35:32 +0200 Subject: [PATCH 06/12] 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 07/12] 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 08/12] 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 09/12] 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 10/12] 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 11/12] 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 12/12] 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