From e3e4ecbc5c51b38686731a03356d5bca87bda1b6 Mon Sep 17 00:00:00 2001 From: RePixelatedMC <107539181+RePixelatedMC@users.noreply.github.com> Date: Sat, 27 Apr 2024 12:41:29 +0200 Subject: [PATCH 01/10] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 52dbc784e..df89fcade 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,6 @@ IrisAccess access=IrisToolbelt.createWorld() // If you like builders... .name("myWorld") // The world name .dimension("terrifyinghands") .seed(69133742) // The world seed - .headless(true) // Headless make gen go fast .pregen(PregenTask // Define a pregen job to run .builder() .center(new Position2(0,0)) // REGION coords (1 region = 32x32 chunks) From a3dcf031c9934be0865b638d581bfe138fd56d5b Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Mon, 29 Apr 2024 15:45:41 -0400 Subject: [PATCH 02/10] v+ , and the other v+ lol --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 77c48256c..53fc6212c 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ plugins { id "de.undercouch.download" version "5.0.1" } -version '3.2.4-1.19.2-1.20.4' +version '3.2.6-1.19.2-1.20.4' def specialSourceVersion = '1.11.0' //[NMS] // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED From 05f4955989a1ea1eb8a16d7489002ae2c83acc27 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 3 May 2024 19:01:33 +0200 Subject: [PATCH 03/10] implement proper datapack reload+per world height --- core/src/main/java/com/volmit/iris/Iris.java | 3 + .../volmit/iris/core/ServerConfigurator.java | 2 +- .../iris/core/commands/CommandStudio.java | 16 +- .../com/volmit/iris/core/nms/INMSBinding.java | 7 +- .../iris/core/nms/v1X/NMSBinding1X.java | 2 +- .../iris/core/tools/IrisWorldCreator.java | 4 + .../iris/core/nms/v1_19_R1/NMSBinding.java | 225 ++++++++++------ .../iris/core/nms/v1_19_R2/NMSBinding.java | 225 ++++++++++------ .../iris/core/nms/v1_19_R3/NMSBinding.java | 223 ++++++++++------ .../iris/core/nms/v1_20_R1/NMSBinding.java | 224 ++++++++++------ .../iris/core/nms/v1_20_R2/NMSBinding.java | 224 ++++++++++------ .../iris/core/nms/v1_20_R3/NMSBinding.java | 247 ++++++++++++------ 12 files changed, 935 insertions(+), 467 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index dbe796d4f..213d96709 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -747,6 +747,9 @@ public class Iris extends VolmitPlugin implements Listener { ff.mkdirs(); service(StudioSVC.class).installIntoWorld(getSender(), dim.getLoadKey(), w.worldFolder()); } + if (!INMS.get().registerDimension(worldName, dim)) { + throw new IllegalStateException("Unable to register dimension " + dim.getName()); + } return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey(), false); } diff --git a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java index b4744cc20..c4d20f73e 100644 --- a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java +++ b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java @@ -228,7 +228,7 @@ public class ServerConfigurator { Iris.info( "Hotloading all Datapacks!"); if (INMS.get().supportsDataPacks()) { for (File folder : getDatapacksFolder()) { - INMS.get().loadDatapack(folder); + INMS.get().loadDatapack(folder, false); } Iris.info("Datapacks Hotloaded!"); Iris.info(C.YELLOW + "============================================================================"); diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java index 71e5b89dd..55ef323ab 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java @@ -20,9 +20,11 @@ package com.volmit.iris.core.commands; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.gui.NoiseExplorerGUI; import com.volmit.iris.core.gui.VisionGUI; import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.project.IrisProject; import com.volmit.iris.core.service.ConversionSVC; import com.volmit.iris.core.service.StudioSVC; @@ -279,13 +281,23 @@ public class CommandStudio implements DecreeExecutor { } @Decree(description = "Hotload a studio", aliases = {"reload", "h"}) - public void hotload() { + public void hotload(@Param(defaultValue = "false") boolean reloadDataPack) { if (!Iris.service(StudioSVC.class).isProjectOpen()) { sender().sendMessage(C.RED + "No studio world open!"); return; } - Iris.service(StudioSVC.class).getActiveProject().getActiveProvider().getEngine().hotload(); + var provider = Iris.service(StudioSVC.class).getActiveProject().getActiveProvider(); + provider.getEngine().hotload(); sender().sendMessage(C.GREEN + "Hotloaded"); + if (reloadDataPack) { + var world = provider.getTarget().getWorld().realWorld(); + if (world == null) { + sender().sendMessage(C.RED + "Failed to reload datapacks."); + return; + } + boolean success = INMS.get().loadDatapack(new File(world.getWorldFolder(), "datapacks"), true); + sender().sendMessage(success ? C.GREEN + "Reloaded datapacks." : C.RED + "Failed to reload datapacks."); + } } @Decree(description = "Show loot if a chest were right here", origin = DecreeOrigin.PLAYER, sync = true) 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 53c71a833..0ed630cf4 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 @@ -19,6 +19,7 @@ package com.volmit.iris.core.nms; import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.mantle.Mantle; @@ -113,7 +114,11 @@ public interface INMSBinding { Entity spawnEntity(Location location, EntityType type, CreatureSpawnEvent.SpawnReason reason); - boolean loadDatapack(File datapackFolder); + boolean loadDatapack(File datapackFolder, boolean replace); + + default boolean registerDimension(String name, IrisDimension dimension) { + return false; + } void injectBukkit(); } 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 77e8e18de..c928db7aa 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 @@ -107,7 +107,7 @@ public class NMSBinding1X implements INMSBinding { } @Override - public boolean loadDatapack(File datapackFolder) { + public boolean loadDatapack(File datapackFolder, boolean replace) { return false; } diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisWorldCreator.java b/core/src/main/java/com/volmit/iris/core/tools/IrisWorldCreator.java index b815d3809..36aa52789 100644 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisWorldCreator.java +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisWorldCreator.java @@ -19,6 +19,7 @@ package com.volmit.iris.core.tools; import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.nms.INMS; import com.volmit.iris.engine.object.*; import com.volmit.iris.engine.platform.BukkitChunkGenerator; import org.bukkit.Bukkit; @@ -84,6 +85,9 @@ public class IrisWorldCreator { ? dim.getLoader().getDataFolder() : new File(w.worldFolder(), "iris/pack"), dimensionName, smartVanillaHeight); + if (!INMS.get().registerDimension(name, dim)) { + throw new IllegalStateException("Unable to register dimension " + dim.getName()); + } return new WorldCreator(name) .environment(findEnvironment()) diff --git a/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java index ca74aad02..3561eb997 100644 --- a/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java +++ b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java @@ -13,13 +13,19 @@ import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Vector; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import com.google.common.base.Preconditions; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import com.mojang.serialization.Lifecycle; +import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.format.C; import com.volmit.iris.util.io.IO; import it.unimi.dsi.fastutil.objects.Reference2IntMap; @@ -29,9 +35,14 @@ import net.bytebuddy.dynamic.loading.ClassReloadingStrategy; import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.MappedRegistry; import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.util.GsonHelper; import net.minecraft.world.level.Level; import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -45,6 +56,7 @@ import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -551,25 +563,34 @@ public class NMSBinding implements INMSBinding { } @Override - public boolean loadDatapack(File folder) { + public boolean registerDimension(String name, IrisDimension dimension) { + var registry = registry(Registry.DIMENSION_TYPE_REGISTRY); + var baseLocation = switch (dimension.getEnvironment()) { + case NORMAL -> new ResourceLocation("minecraft", "overworld"); + case NETHER -> new ResourceLocation("minecraft", "the_nether"); + case THE_END -> new ResourceLocation("minecraft", "the_end"); + case CUSTOM -> throw new IllegalArgumentException("Cannot register custom dimension"); + }; + var base = registry.getHolder(ResourceKey.create(Registry.DIMENSION_TYPE_REGISTRY, baseLocation)).orElse(null); + if (base == null) return false; + var json = encode(DimensionType.CODEC, base).orElse(null); + if (json == null) return false; + var object = json.getAsJsonObject(); + var height = dimension.getDimensionHeight(); + object.addProperty("min_y", height.getMin()); + object.addProperty("height", height.getMax() - height.getMin()); + object.addProperty("logical_height", dimension.getLogicalHeight()); + var value = decode(DimensionType.CODEC, object.toString()).map(Holder::value).orElse(null); + if (value == null) return false; + return register(Registry.DIMENSION_TYPE_REGISTRY, new ResourceLocation("iris", name), value, true); + } + + @Override + public boolean loadDatapack(File folder, boolean replace) { var data = new File(folder, "iris/data"); if (!data.exists() || !data.isDirectory()) return false; FilenameFilter jsonFilter = (dir, name) -> new File(dir, name).isFile() && name.toLowerCase().endsWith(".json"); - var dimensionFolder = new File(data, "minecraft/dimension_type"); - if (dimensionFolder.exists()) { - var files = dimensionFolder.listFiles(jsonFilter); - if (files != null) { - for (File file : files) { - try { - modifyDimension(file); - } catch (Throwable e) { - Iris.error("Unable to modify dimension!"); - e.printStackTrace(); - } - } - } - } var files = data.listFiles((dir, name) -> new File(dir, name).isDirectory()); if (files == null) return false; for (File file : files) { @@ -579,7 +600,8 @@ public class NMSBinding implements INMSBinding { if (biomeFiles == null) continue; for (File biomeFile : biomeFiles) { try { - registerBiome(file.getName(), biomeFile); + var value = decode(net.minecraft.world.level.biome.Biome.CODEC, IO.readAll(file)).map(Holder::value).orElse(null); + register(Registry.BIOME_REGISTRY, from(file.getName(), biomeFile), value, replace); } catch (Throwable e) { Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName()); e.printStackTrace(); @@ -594,75 +616,95 @@ public class NMSBinding implements INMSBinding { return new ResourceLocation(namespace, name.substring(0, name.lastIndexOf('.'))); } - private void registerBiome(String namespace, File file) throws Throwable { - var rawRegistry = registry().registry(Registry.BIOME_REGISTRY).orElse(null); - var key = ResourceKey.create(Registry.BIOME_REGISTRY, from(namespace, file)); - if (!(rawRegistry instanceof MappedRegistry registry)) - throw new IllegalStateException("The Biome Registry is not a mapped Registry!"); - if (registry.containsKey(key)) return; - Field field = getField(MappedRegistry.class, boolean.class); - field.setAccessible(true); - boolean frozen = field.getBoolean(registry); - field.setBoolean(registry, false); - Field holdersField = null; - boolean holders = false; - for (Field f : MappedRegistry.class.getDeclaredFields()) { - if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); - } + private Optional decode(Codec codec, String json) { + return codec.decode(JsonOps.INSTANCE, GsonHelper.parse(json)).get().left().map(Pair::getFirst); + } + private Optional encode(Codec codec, T value) { + return codec.encode(value, JsonOps.INSTANCE, new JsonObject()).result(); + } + + private boolean register(ResourceKey> registryKey, ResourceLocation location, T value, boolean replace) { + Preconditions.checkArgument(registryKey != null, "The registry cannot be null!"); + Preconditions.checkArgument(location != null, "The location cannot be null!"); + Preconditions.checkArgument(value != null, "The value cannot be null!"); + var registry = registry(registryKey); + var key = ResourceKey.create(registryKey, location); try { - var biome = net.minecraft.world.level.biome.Biome.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file))) - .get().left().map(Pair::getFirst).map(Holder::value).orElse(null); - if (biome == null) - throw new IllegalStateException("Failed to decode biome " + file.getName()); - - registry.createIntrusiveHolder(biome); - registry.register(key, biome, Lifecycle.stable()); - } finally { - field.setBoolean(registry, frozen); - if (holders) { - holdersField.set(registry, null); + if (registry.containsKey(key)) { + if (!replace) return false; + return replace(registryKey, location, value); } + Field field = getField(MappedRegistry.class, boolean.class); + field.setAccessible(true); + boolean frozen = field.getBoolean(registry); + field.setBoolean(registry, false); + Field holdersField = null; + boolean holders = false; + for (Field f : MappedRegistry.class.getDeclaredFields()) { + if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); + } + + try { + registry.createIntrusiveHolder(value); + registry.register(key, value, Lifecycle.stable()); + return true; + } finally { + field.setBoolean(registry, frozen); + if (holders) { + holdersField.set(registry, null); + } + } + } catch (Throwable e) { + throw new IllegalStateException(e); } } @SuppressWarnings("unchecked") - private void modifyDimension(File file) throws Throwable { - var key = ResourceKey.create(Registry.DIMENSION_TYPE_REGISTRY, from("minecraft", file)); - var rawRegistry = registry().registry(Registry.DIMENSION_TYPE_REGISTRY).orElse(null); - if (!(rawRegistry instanceof MappedRegistry registry)) - throw new IllegalStateException("The Dimension Registry is not a mapped Registry!"); + private boolean replace(ResourceKey> registryKey, ResourceLocation location, T value) { + Preconditions.checkArgument(registryKey != null, "The registryKey cannot be null!"); + Preconditions.checkArgument(location != null, "The location cannot be null!"); + Preconditions.checkArgument(value != null, "The value cannot be null!"); + var registry = registry(registryKey); + var key = ResourceKey.create(registryKey, location); + try { + var holder = registry.getHolder(key).orElse(null); + if (holder == null) return false; + var oldValue = holder.value(); + Field valueField = getField(Holder.Reference.class, "T"); + valueField.setAccessible(true); + Field toIdField = getField(MappedRegistry.class, buildType(Reference2IntMap.class, "T")); + toIdField.setAccessible(true); + Field byValueField = getField(MappedRegistry.class, buildType(Map.class, "T", buildType(Holder.Reference.class, "T"))); + byValueField.setAccessible(true); + Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName())); + lifecyclesField.setAccessible(true); + var toId = (Reference2IntMap) toIdField.get(registry); + var byValue = (Map>) byValueField.get(registry); + var lifecycles = (Map) lifecyclesField.get(registry); - var holder = registry.getHolder(key).orElseThrow(() -> new IllegalStateException("Unknown dimension type: " + key)); - var oldValue = holder.value(); - Field valueField = getField(Holder.Reference.class, "T"); - valueField.setAccessible(true); - Field toIdField = getField(MappedRegistry.class, buildType(Reference2IntMap.class, "T")); - toIdField.setAccessible(true); - Field byValueField = getField(MappedRegistry.class, buildType(Map.class, "T", buildType(Holder.Reference.class, "T"))); - byValueField.setAccessible(true); - Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName())); - lifecyclesField.setAccessible(true); - var toId = (Reference2IntMap) toIdField.get(registry); - var byValue = (Map>) byValueField.get(registry); - var lifecycles = (Map) lifecyclesField.get(registry); + valueField.set(holder, value); + toId.put(value, toId.removeInt(oldValue)); + byValue.put(value, byValue.remove(oldValue)); + lifecycles.put(value, lifecycles.remove(oldValue)); + return true; + } catch (Throwable e) { + throw new IllegalStateException(e); + } + } - var newValue = DimensionType.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file))) - .get().left().map(Pair::getFirst).map(Holder::value).orElse(null); - if (newValue == null) - throw new IllegalArgumentException("Failed to parse dimension type " + key.location() + " from " + file); - - valueField.set(holder, newValue); - toId.put(newValue, toId.removeInt(oldValue)); - byValue.put(newValue, byValue.remove(oldValue)); - lifecycles.put(newValue, lifecycles.remove(oldValue)); + private MappedRegistry registry(ResourceKey> registryKey) { + var rawRegistry = registry().registry(registryKey).orElse(null); + if (!(rawRegistry instanceof MappedRegistry registry)) + throw new IllegalStateException("The Registry is not a mapped Registry!"); + return registry; } private static String buildType(Class clazz, String... parameterTypes) { @@ -715,6 +757,13 @@ public class NMSBinding implements INMSBinding { .visit(Advice.to(WorldCreatorAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(String.class)))) .make() .load(WorldCreator.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); + new ByteBuddy() + .redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, + PrimaryLevelData.class, ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, + List.class, boolean.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); Iris.info("Injected Bukkit Successfully!"); } catch (Exception e) { Iris.info(C.RED + "Failed to Inject Bukkit!"); @@ -724,6 +773,28 @@ public class NMSBinding implements INMSBinding { } + private static class ServerLevelAdvice { + @Advice.OnMethodEnter + static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { + File iris = new File(access.levelDirectory.path().toFile(), "iris"); + if (!iris.exists()) return; + var logger = MinecraftServer.LOGGER; + ResourceKey typeKey = ResourceKey.create(Registry.DIMENSION_TYPE_REGISTRY, new ResourceLocation("iris", key.location().getPath())); + RegistryAccess registryAccess = server.registryAccess(); + Registry registry = registryAccess.registry(Registry.DIMENSION_TYPE_REGISTRY).orElse(null); + if (registry == null) { + logger.warn("Unable to find registry for dimension type {}", typeKey); + return; + } + Holder holder = registry.getHolder(typeKey).orElse(null); + if (holder == null) { + logger.warn("Unable to find dimension type {}", typeKey); + return; + } + levelStem = new LevelStem(holder, levelStem.generator()); + } + } + private static class WorldCreatorAdvice { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) String name) { @@ -741,4 +812,4 @@ public class NMSBinding implements INMSBinding { } } } -} +} \ No newline at end of file diff --git a/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java index c58eb3143..03105825c 100644 --- a/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java +++ b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java @@ -13,13 +13,19 @@ import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Vector; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import com.google.common.base.Preconditions; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import com.mojang.serialization.Lifecycle; +import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.format.C; import com.volmit.iris.util.io.IO; import it.unimi.dsi.fastutil.objects.Reference2IntMap; @@ -29,9 +35,14 @@ import net.bytebuddy.dynamic.loading.ClassReloadingStrategy; import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.MappedRegistry; import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.util.GsonHelper; import net.minecraft.world.level.Level; import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -45,6 +56,7 @@ import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack; import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -553,25 +565,34 @@ public class NMSBinding implements INMSBinding { } @Override - public boolean loadDatapack(File folder) { + public boolean registerDimension(String name, IrisDimension dimension) { + var registry = registry(Registries.DIMENSION_TYPE); + var baseLocation = switch (dimension.getEnvironment()) { + case NORMAL -> new ResourceLocation("minecraft", "overworld"); + case NETHER -> new ResourceLocation("minecraft", "the_nether"); + case THE_END -> new ResourceLocation("minecraft", "the_end"); + case CUSTOM -> throw new IllegalArgumentException("Cannot register custom dimension"); + }; + var base = registry.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, baseLocation)).orElse(null); + if (base == null) return false; + var json = encode(DimensionType.CODEC, base).orElse(null); + if (json == null) return false; + var object = json.getAsJsonObject(); + var height = dimension.getDimensionHeight(); + object.addProperty("min_y", height.getMin()); + object.addProperty("height", height.getMax() - height.getMin()); + object.addProperty("logical_height", dimension.getLogicalHeight()); + var value = decode(DimensionType.CODEC, object.toString()).map(Holder::value).orElse(null); + if (value == null) return false; + return register(Registries.DIMENSION_TYPE, new ResourceLocation("iris", name), value, true); + } + + @Override + public boolean loadDatapack(File folder, boolean replace) { var data = new File(folder, "iris/data"); if (!data.exists() || !data.isDirectory()) return false; FilenameFilter jsonFilter = (dir, name) -> new File(dir, name).isFile() && name.toLowerCase().endsWith(".json"); - var dimensionFolder = new File(data, "minecraft/dimension_type"); - if (dimensionFolder.exists()) { - var files = dimensionFolder.listFiles(jsonFilter); - if (files != null) { - for (File file : files) { - try { - modifyDimension(file); - } catch (Throwable e) { - Iris.error("Unable to modify dimension!"); - e.printStackTrace(); - } - } - } - } var files = data.listFiles((dir, name) -> new File(dir, name).isDirectory()); if (files == null) return false; for (File file : files) { @@ -581,7 +602,8 @@ public class NMSBinding implements INMSBinding { if (biomeFiles == null) continue; for (File biomeFile : biomeFiles) { try { - registerBiome(file.getName(), biomeFile); + var value = decode(net.minecraft.world.level.biome.Biome.CODEC, IO.readAll(file)).map(Holder::value).orElse(null); + register(Registries.BIOME, from(file.getName(), biomeFile), value, replace); } catch (Throwable e) { Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName()); e.printStackTrace(); @@ -596,75 +618,95 @@ public class NMSBinding implements INMSBinding { return new ResourceLocation(namespace, name.substring(0, name.lastIndexOf('.'))); } - private void registerBiome(String namespace, File file) throws Throwable { - var rawRegistry = registry().registry(Registries.BIOME).orElse(null); - var key = ResourceKey.create(Registries.BIOME, from(namespace, file)); - if (!(rawRegistry instanceof MappedRegistry registry)) - throw new IllegalStateException("The Biome Registry is not a mapped Registry!"); - if (registry.containsKey(key)) return; - Field field = getField(MappedRegistry.class, boolean.class); - field.setAccessible(true); - boolean frozen = field.getBoolean(registry); - field.setBoolean(registry, false); - Field holdersField = null; - boolean holders = false; - for (Field f : MappedRegistry.class.getDeclaredFields()) { - if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); - } + private Optional decode(Codec codec, String json) { + return codec.decode(JsonOps.INSTANCE, GsonHelper.parse(json)).get().left().map(Pair::getFirst); + } + private Optional encode(Codec codec, T value) { + return codec.encode(value, JsonOps.INSTANCE, new JsonObject()).result(); + } + + private boolean register(ResourceKey> registryKey, ResourceLocation location, T value, boolean replace) { + Preconditions.checkArgument(registryKey != null, "The registry cannot be null!"); + Preconditions.checkArgument(location != null, "The location cannot be null!"); + Preconditions.checkArgument(value != null, "The value cannot be null!"); + var registry = registry(registryKey); + var key = ResourceKey.create(registryKey, location); try { - var biome = net.minecraft.world.level.biome.Biome.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file))) - .get().left().map(Pair::getFirst).map(Holder::value).orElse(null); - if (biome == null) - throw new IllegalStateException("Failed to decode biome " + file.getName()); - - registry.createIntrusiveHolder(biome); - registry.register(key, biome, Lifecycle.stable()); - } finally { - field.setBoolean(registry, frozen); - if (holders) { - holdersField.set(registry, null); + if (registry.containsKey(key)) { + if (!replace) return false; + return replace(registryKey, location, value); } + Field field = getField(MappedRegistry.class, boolean.class); + field.setAccessible(true); + boolean frozen = field.getBoolean(registry); + field.setBoolean(registry, false); + Field holdersField = null; + boolean holders = false; + for (Field f : MappedRegistry.class.getDeclaredFields()) { + if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); + } + + try { + registry.createIntrusiveHolder(value); + registry.register(key, value, Lifecycle.stable()); + return true; + } finally { + field.setBoolean(registry, frozen); + if (holders) { + holdersField.set(registry, null); + } + } + } catch (Throwable e) { + throw new IllegalStateException(e); } } @SuppressWarnings("unchecked") - private void modifyDimension(File file) throws Throwable { - var key = ResourceKey.create(Registries.DIMENSION_TYPE, from("minecraft", file)); - var rawRegistry = registry().registry(Registries.DIMENSION_TYPE).orElse(null); - if (!(rawRegistry instanceof MappedRegistry registry)) - throw new IllegalStateException("The Dimension Registry is not a mapped Registry!"); + private boolean replace(ResourceKey> registryKey, ResourceLocation location, T value) { + Preconditions.checkArgument(registryKey != null, "The registryKey cannot be null!"); + Preconditions.checkArgument(location != null, "The location cannot be null!"); + Preconditions.checkArgument(value != null, "The value cannot be null!"); + var registry = registry(registryKey); + var key = ResourceKey.create(registryKey, location); + try { + var holder = registry.getHolder(key).orElse(null); + if (holder == null) return false; + var oldValue = holder.value(); + Field valueField = getField(Holder.Reference.class, "T"); + valueField.setAccessible(true); + Field toIdField = getField(MappedRegistry.class, buildType(Reference2IntMap.class, "T")); + toIdField.setAccessible(true); + Field byValueField = getField(MappedRegistry.class, buildType(Map.class, "T", buildType(Holder.Reference.class, "T"))); + byValueField.setAccessible(true); + Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName())); + lifecyclesField.setAccessible(true); + var toId = (Reference2IntMap) toIdField.get(registry); + var byValue = (Map>) byValueField.get(registry); + var lifecycles = (Map) lifecyclesField.get(registry); - var holder = registry.getHolder(key).orElseThrow(() -> new IllegalStateException("Unknown dimension type: " + key)); - var oldValue = holder.value(); - Field valueField = getField(Holder.Reference.class, "T"); - valueField.setAccessible(true); - Field toIdField = getField(MappedRegistry.class, buildType(Reference2IntMap.class, "T")); - toIdField.setAccessible(true); - Field byValueField = getField(MappedRegistry.class, buildType(Map.class, "T", buildType(Holder.Reference.class, "T"))); - byValueField.setAccessible(true); - Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName())); - lifecyclesField.setAccessible(true); - var toId = (Reference2IntMap) toIdField.get(registry); - var byValue = (Map>) byValueField.get(registry); - var lifecycles = (Map) lifecyclesField.get(registry); + valueField.set(holder, value); + toId.put(value, toId.removeInt(oldValue)); + byValue.put(value, byValue.remove(oldValue)); + lifecycles.put(value, lifecycles.remove(oldValue)); + return true; + } catch (Throwable e) { + throw new IllegalStateException(e); + } + } - var newValue = DimensionType.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file))) - .get().left().map(Pair::getFirst).map(Holder::value).orElse(null); - if (newValue == null) - throw new IllegalArgumentException("Failed to parse dimension type " + key.location() + " from " + file); - - valueField.set(holder, newValue); - toId.put(newValue, toId.removeInt(oldValue)); - byValue.put(newValue, byValue.remove(oldValue)); - lifecycles.put(newValue, lifecycles.remove(oldValue)); + private MappedRegistry registry(ResourceKey> registryKey) { + var rawRegistry = registry().registry(registryKey).orElse(null); + if (!(rawRegistry instanceof MappedRegistry registry)) + throw new IllegalStateException("The Registry is not a mapped Registry!"); + return registry; } private static String buildType(Class clazz, String... parameterTypes) { @@ -716,6 +758,13 @@ public class NMSBinding implements INMSBinding { .visit(Advice.to(WorldCreatorAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(String.class)))) .make() .load(WorldCreator.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); + new ByteBuddy() + .redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, + PrimaryLevelData.class, ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, + List.class, boolean.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); Iris.info("Injected Bukkit Successfully!"); } catch (Exception e) { Iris.info(C.RED + "Failed to Inject Bukkit!"); @@ -725,6 +774,28 @@ public class NMSBinding implements INMSBinding { } + private static class ServerLevelAdvice { + @Advice.OnMethodEnter + static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { + File iris = new File(access.levelDirectory.path().toFile(), "iris"); + if (!iris.exists()) return; + var logger = MinecraftServer.LOGGER; + ResourceKey typeKey = ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", key.location().getPath())); + RegistryAccess registryAccess = server.registryAccess(); + Registry registry = registryAccess.registry(Registries.DIMENSION_TYPE).orElse(null); + if (registry == null) { + logger.warn("Unable to find registry for dimension type {}", typeKey); + return; + } + Holder holder = registry.getHolder(typeKey).orElse(null); + if (holder == null) { + logger.warn("Unable to find dimension type {}", typeKey); + return; + } + levelStem = new LevelStem(holder, levelStem.generator()); + } + } + private static class WorldCreatorAdvice { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) String name) { @@ -742,4 +813,4 @@ public class NMSBinding implements INMSBinding { } } } -} +} \ No newline at end of file diff --git a/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java index bf1f82c6e..8eef4e427 100644 --- a/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java +++ b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java @@ -13,13 +13,19 @@ import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Vector; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import com.google.common.base.Preconditions; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import com.mojang.serialization.Lifecycle; +import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.format.C; import com.volmit.iris.util.io.IO; import it.unimi.dsi.fastutil.objects.Reference2IntMap; @@ -29,9 +35,14 @@ import net.bytebuddy.dynamic.loading.ClassReloadingStrategy; import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.MappedRegistry; import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.util.GsonHelper; import net.minecraft.world.level.Level; import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -45,6 +56,7 @@ import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -557,25 +569,34 @@ public class NMSBinding implements INMSBinding { } @Override - public boolean loadDatapack(File folder) { + public boolean registerDimension(String name, IrisDimension dimension) { + var registry = registry(Registries.DIMENSION_TYPE); + var baseLocation = switch (dimension.getEnvironment()) { + case NORMAL -> new ResourceLocation("minecraft", "overworld"); + case NETHER -> new ResourceLocation("minecraft", "the_nether"); + case THE_END -> new ResourceLocation("minecraft", "the_end"); + case CUSTOM -> throw new IllegalArgumentException("Cannot register custom dimension"); + }; + var base = registry.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, baseLocation)).orElse(null); + if (base == null) return false; + var json = encode(DimensionType.CODEC, base).orElse(null); + if (json == null) return false; + var object = json.getAsJsonObject(); + var height = dimension.getDimensionHeight(); + object.addProperty("min_y", height.getMin()); + object.addProperty("height", height.getMax() - height.getMin()); + object.addProperty("logical_height", dimension.getLogicalHeight()); + var value = decode(DimensionType.CODEC, object.toString()).map(Holder::value).orElse(null); + if (value == null) return false; + return register(Registries.DIMENSION_TYPE, new ResourceLocation("iris", name), value, true); + } + + @Override + public boolean loadDatapack(File folder, boolean replace) { var data = new File(folder, "iris/data"); if (!data.exists() || !data.isDirectory()) return false; FilenameFilter jsonFilter = (dir, name) -> new File(dir, name).isFile() && name.toLowerCase().endsWith(".json"); - var dimensionFolder = new File(data, "minecraft/dimension_type"); - if (dimensionFolder.exists()) { - var files = dimensionFolder.listFiles(jsonFilter); - if (files != null) { - for (File file : files) { - try { - modifyDimension(file); - } catch (Throwable e) { - Iris.error("Unable to modify dimension!"); - e.printStackTrace(); - } - } - } - } var files = data.listFiles((dir, name) -> new File(dir, name).isDirectory()); if (files == null) return false; for (File file : files) { @@ -585,7 +606,8 @@ public class NMSBinding implements INMSBinding { if (biomeFiles == null) continue; for (File biomeFile : biomeFiles) { try { - registerBiome(file.getName(), biomeFile); + var value = decode(net.minecraft.world.level.biome.Biome.CODEC, IO.readAll(file)).map(Holder::value).orElse(null); + register(Registries.BIOME, from(file.getName(), biomeFile), value, replace); } catch (Throwable e) { Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName()); e.printStackTrace(); @@ -600,75 +622,95 @@ public class NMSBinding implements INMSBinding { return new ResourceLocation(namespace, name.substring(0, name.lastIndexOf('.'))); } - private void registerBiome(String namespace, File file) throws Throwable { - var rawRegistry = registry().registry(Registries.BIOME).orElse(null); - var key = ResourceKey.create(Registries.BIOME, from(namespace, file)); - if (!(rawRegistry instanceof MappedRegistry registry)) - throw new IllegalStateException("The Biome Registry is not a mapped Registry!"); - if (registry.containsKey(key)) return; - Field field = getField(MappedRegistry.class, boolean.class); - field.setAccessible(true); - boolean frozen = field.getBoolean(registry); - field.setBoolean(registry, false); - Field holdersField = null; - boolean holders = false; - for (Field f : MappedRegistry.class.getDeclaredFields()) { - if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); - } + private Optional decode(Codec codec, String json) { + return codec.decode(JsonOps.INSTANCE, GsonHelper.parse(json)).get().left().map(Pair::getFirst); + } + private Optional encode(Codec codec, T value) { + return codec.encode(value, JsonOps.INSTANCE, new JsonObject()).result(); + } + + private boolean register(ResourceKey> registryKey, ResourceLocation location, T value, boolean replace) { + Preconditions.checkArgument(registryKey != null, "The registry cannot be null!"); + Preconditions.checkArgument(location != null, "The location cannot be null!"); + Preconditions.checkArgument(value != null, "The value cannot be null!"); + var registry = registry(registryKey); + var key = ResourceKey.create(registryKey, location); try { - var biome = net.minecraft.world.level.biome.Biome.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file))) - .get().left().map(Pair::getFirst).map(Holder::value).orElse(null); - if (biome == null) - throw new IllegalStateException("Failed to decode biome " + file.getName()); - - registry.createIntrusiveHolder(biome); - registry.register(key, biome, Lifecycle.stable()); - } finally { - field.setBoolean(registry, frozen); - if (holders) { - holdersField.set(registry, null); + if (registry.containsKey(key)) { + if (!replace) return false; + return replace(registryKey, location, value); } + Field field = getField(MappedRegistry.class, boolean.class); + field.setAccessible(true); + boolean frozen = field.getBoolean(registry); + field.setBoolean(registry, false); + Field holdersField = null; + boolean holders = false; + for (Field f : MappedRegistry.class.getDeclaredFields()) { + if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); + } + + try { + registry.createIntrusiveHolder(value); + registry.register(key, value, Lifecycle.stable()); + return true; + } finally { + field.setBoolean(registry, frozen); + if (holders) { + holdersField.set(registry, null); + } + } + } catch (Throwable e) { + throw new IllegalStateException(e); } } @SuppressWarnings("unchecked") - private void modifyDimension(File file) throws Throwable { - var key = ResourceKey.create(Registries.DIMENSION_TYPE, from("minecraft", file)); - var rawRegistry = registry().registry(Registries.DIMENSION_TYPE).orElse(null); - if (!(rawRegistry instanceof MappedRegistry registry)) - throw new IllegalStateException("The Dimension Registry is not a mapped Registry!"); + private boolean replace(ResourceKey> registryKey, ResourceLocation location, T value) { + Preconditions.checkArgument(registryKey != null, "The registryKey cannot be null!"); + Preconditions.checkArgument(location != null, "The location cannot be null!"); + Preconditions.checkArgument(value != null, "The value cannot be null!"); + var registry = registry(registryKey); + var key = ResourceKey.create(registryKey, location); + try { + var holder = registry.getHolder(key).orElse(null); + if (holder == null) return false; + var oldValue = holder.value(); + Field valueField = getField(Holder.Reference.class, "T"); + valueField.setAccessible(true); + Field toIdField = getField(MappedRegistry.class, buildType(Reference2IntMap.class, "T")); + toIdField.setAccessible(true); + Field byValueField = getField(MappedRegistry.class, buildType(Map.class, "T", buildType(Holder.Reference.class, "T"))); + byValueField.setAccessible(true); + Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName())); + lifecyclesField.setAccessible(true); + var toId = (Reference2IntMap) toIdField.get(registry); + var byValue = (Map>) byValueField.get(registry); + var lifecycles = (Map) lifecyclesField.get(registry); - var holder = registry.getHolder(key).orElseThrow(() -> new IllegalStateException("Unknown dimension type: " + key)); - var oldValue = holder.value(); - Field valueField = getField(Holder.Reference.class, "T"); - valueField.setAccessible(true); - Field toIdField = getField(MappedRegistry.class, buildType(Reference2IntMap.class, "T")); - toIdField.setAccessible(true); - Field byValueField = getField(MappedRegistry.class, buildType(Map.class, "T", buildType(Holder.Reference.class, "T"))); - byValueField.setAccessible(true); - Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName())); - lifecyclesField.setAccessible(true); - var toId = (Reference2IntMap) toIdField.get(registry); - var byValue = (Map>) byValueField.get(registry); - var lifecycles = (Map) lifecyclesField.get(registry); + valueField.set(holder, value); + toId.put(value, toId.removeInt(oldValue)); + byValue.put(value, byValue.remove(oldValue)); + lifecycles.put(value, lifecycles.remove(oldValue)); + return true; + } catch (Throwable e) { + throw new IllegalStateException(e); + } + } - var newValue = DimensionType.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file))) - .get().left().map(Pair::getFirst).map(Holder::value).orElse(null); - if (newValue == null) - throw new IllegalArgumentException("Failed to parse dimension type " + key.location() + " from " + file); - - valueField.set(holder, newValue); - toId.put(newValue, toId.removeInt(oldValue)); - byValue.put(newValue, byValue.remove(oldValue)); - lifecycles.put(newValue, lifecycles.remove(oldValue)); + private MappedRegistry registry(ResourceKey> registryKey) { + var rawRegistry = registry().registry(registryKey).orElse(null); + if (!(rawRegistry instanceof MappedRegistry registry)) + throw new IllegalStateException("The Registry is not a mapped Registry!"); + return registry; } private static String buildType(Class clazz, String... parameterTypes) { @@ -720,6 +762,13 @@ public class NMSBinding implements INMSBinding { .visit(Advice.to(WorldCreatorAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(String.class)))) .make() .load(WorldCreator.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); + new ByteBuddy() + .redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, + PrimaryLevelData.class, ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, + List.class, boolean.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); Iris.info("Injected Bukkit Successfully!"); } catch (Exception e) { Iris.info(C.RED + "Failed to Inject Bukkit!"); @@ -729,6 +778,28 @@ public class NMSBinding implements INMSBinding { } + private static class ServerLevelAdvice { + @Advice.OnMethodEnter + static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { + File iris = new File(access.levelDirectory.path().toFile(), "iris"); + if (!iris.exists()) return; + var logger = MinecraftServer.LOGGER; + ResourceKey typeKey = ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", key.location().getPath())); + RegistryAccess registryAccess = server.registryAccess(); + Registry registry = registryAccess.registry(Registries.DIMENSION_TYPE).orElse(null); + if (registry == null) { + logger.warn("Unable to find registry for dimension type {}", typeKey); + return; + } + Holder holder = registry.getHolder(typeKey).orElse(null); + if (holder == null) { + logger.warn("Unable to find dimension type {}", typeKey); + return; + } + levelStem = new LevelStem(holder, levelStem.generator()); + } + } + private static class WorldCreatorAdvice { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) String name) { 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 eb4fb4162..7e6e8c963 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 @@ -1,14 +1,18 @@ package com.volmit.iris.core.nms.v1_20_R1; import com.google.common.base.Preconditions; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import com.mojang.serialization.Lifecycle; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.format.C; @@ -38,8 +42,11 @@ import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.TagParser; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.util.GsonHelper; +import net.minecraft.world.RandomSequences; import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.BiomeSource; @@ -50,6 +57,9 @@ import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -66,6 +76,7 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.entity.EntityType; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -84,7 +95,9 @@ import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Vector; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; public class NMSBinding implements INMSBinding { @@ -543,25 +556,34 @@ public class NMSBinding implements INMSBinding { } @Override - public boolean loadDatapack(File folder) { + public boolean registerDimension(String name, IrisDimension dimension) { + var registry = registry(Registries.DIMENSION_TYPE); + var baseLocation = switch (dimension.getEnvironment()) { + case NORMAL -> new ResourceLocation("minecraft", "overworld"); + case NETHER -> new ResourceLocation("minecraft", "the_nether"); + case THE_END -> new ResourceLocation("minecraft", "the_end"); + case CUSTOM -> throw new IllegalArgumentException("Cannot register custom dimension"); + }; + var base = registry.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, baseLocation)).orElse(null); + if (base == null) return false; + var json = encode(DimensionType.CODEC, base).orElse(null); + if (json == null) return false; + var object = json.getAsJsonObject(); + var height = dimension.getDimensionHeight(); + object.addProperty("min_y", height.getMin()); + object.addProperty("height", height.getMax() - height.getMin()); + object.addProperty("logical_height", dimension.getLogicalHeight()); + var value = decode(DimensionType.CODEC, object.toString()).map(Holder::value).orElse(null); + if (value == null) return false; + return register(Registries.DIMENSION_TYPE, new ResourceLocation("iris", name), value, true); + } + + @Override + public boolean loadDatapack(File folder, boolean replace) { var data = new File(folder, "iris/data"); if (!data.exists() || !data.isDirectory()) return false; FilenameFilter jsonFilter = (dir, name) -> new File(dir, name).isFile() && name.toLowerCase().endsWith(".json"); - var dimensionFolder = new File(data, "minecraft/dimension_type"); - if (dimensionFolder.exists()) { - var files = dimensionFolder.listFiles(jsonFilter); - if (files != null) { - for (File file : files) { - try { - modifyDimension(file); - } catch (Throwable e) { - Iris.error("Unable to modify dimension!"); - e.printStackTrace(); - } - } - } - } var files = data.listFiles((dir, name) -> new File(dir, name).isDirectory()); if (files == null) return false; for (File file : files) { @@ -571,7 +593,8 @@ public class NMSBinding implements INMSBinding { if (biomeFiles == null) continue; for (File biomeFile : biomeFiles) { try { - registerBiome(file.getName(), biomeFile); + var value = decode(net.minecraft.world.level.biome.Biome.CODEC, IO.readAll(file)).map(Holder::value).orElse(null); + register(Registries.BIOME, from(file.getName(), biomeFile), value, replace); } catch (Throwable e) { Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName()); e.printStackTrace(); @@ -586,75 +609,95 @@ public class NMSBinding implements INMSBinding { return new ResourceLocation(namespace, name.substring(0, name.lastIndexOf('.'))); } - private void registerBiome(String namespace, File file) throws Throwable { - var rawRegistry = registry().registry(Registries.BIOME).orElse(null); - var key = ResourceKey.create(Registries.BIOME, from(namespace, file)); - if (!(rawRegistry instanceof MappedRegistry registry)) - throw new IllegalStateException("The Biome Registry is not a mapped Registry!"); - if (registry.containsKey(key)) return; - Field field = getField(MappedRegistry.class, boolean.class); - field.setAccessible(true); - boolean frozen = field.getBoolean(registry); - field.setBoolean(registry, false); - Field holdersField = null; - boolean holders = false; - for (Field f : MappedRegistry.class.getDeclaredFields()) { - if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); - } + private Optional decode(Codec codec, String json) { + return codec.decode(JsonOps.INSTANCE, GsonHelper.parse(json)).get().left().map(Pair::getFirst); + } + private Optional encode(Codec codec, T value) { + return codec.encode(value, JsonOps.INSTANCE, new JsonObject()).result(); + } + + private boolean register(ResourceKey> registryKey, ResourceLocation location, T value, boolean replace) { + Preconditions.checkArgument(registryKey != null, "The registry cannot be null!"); + Preconditions.checkArgument(location != null, "The location cannot be null!"); + Preconditions.checkArgument(value != null, "The value cannot be null!"); + var registry = registry(registryKey); + var key = ResourceKey.create(registryKey, location); try { - var biome = net.minecraft.world.level.biome.Biome.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file))) - .get().left().map(Pair::getFirst).map(Holder::value).orElse(null); - if (biome == null) - throw new IllegalStateException("Failed to decode biome " + file.getName()); - - registry.createIntrusiveHolder(biome); - registry.register(key, biome, Lifecycle.stable()); - } finally { - field.setBoolean(registry, frozen); - if (holders) { - holdersField.set(registry, null); + if (registry.containsKey(key)) { + if (!replace) return false; + return replace(registryKey, location, value); } + Field field = getField(MappedRegistry.class, boolean.class); + field.setAccessible(true); + boolean frozen = field.getBoolean(registry); + field.setBoolean(registry, false); + Field holdersField = null; + boolean holders = false; + for (Field f : MappedRegistry.class.getDeclaredFields()) { + if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); + } + + try { + registry.createIntrusiveHolder(value); + registry.register(key, value, Lifecycle.stable()); + return true; + } finally { + field.setBoolean(registry, frozen); + if (holders) { + holdersField.set(registry, null); + } + } + } catch (Throwable e) { + throw new IllegalStateException(e); } } @SuppressWarnings("unchecked") - private void modifyDimension(File file) throws Throwable { - var key = ResourceKey.create(Registries.DIMENSION_TYPE, from("minecraft", file)); - var rawRegistry = registry().registry(Registries.DIMENSION_TYPE).orElse(null); - if (!(rawRegistry instanceof MappedRegistry registry)) - throw new IllegalStateException("The Dimension Registry is not a mapped Registry!"); + private boolean replace(ResourceKey> registryKey, ResourceLocation location, T value) { + Preconditions.checkArgument(registryKey != null, "The registryKey cannot be null!"); + Preconditions.checkArgument(location != null, "The location cannot be null!"); + Preconditions.checkArgument(value != null, "The value cannot be null!"); + var registry = registry(registryKey); + var key = ResourceKey.create(registryKey, location); + try { + var holder = registry.getHolder(key).orElse(null); + if (holder == null) return false; + var oldValue = holder.value(); + Field valueField = getField(Holder.Reference.class, "T"); + valueField.setAccessible(true); + Field toIdField = getField(MappedRegistry.class, buildType(Reference2IntMap.class, "T")); + toIdField.setAccessible(true); + Field byValueField = getField(MappedRegistry.class, buildType(Map.class, "T", buildType(Holder.Reference.class, "T"))); + byValueField.setAccessible(true); + Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName())); + lifecyclesField.setAccessible(true); + var toId = (Reference2IntMap) toIdField.get(registry); + var byValue = (Map>) byValueField.get(registry); + var lifecycles = (Map) lifecyclesField.get(registry); - var holder = registry.getHolder(key).orElseThrow(() -> new IllegalStateException("Unknown dimension type: " + key)); - var oldValue = holder.value(); - Field valueField = getField(Holder.Reference.class, "T"); - valueField.setAccessible(true); - Field toIdField = getField(MappedRegistry.class, buildType(Reference2IntMap.class, "T")); - toIdField.setAccessible(true); - Field byValueField = getField(MappedRegistry.class, buildType(Map.class, "T", buildType(Holder.Reference.class, "T"))); - byValueField.setAccessible(true); - Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName())); - lifecyclesField.setAccessible(true); - var toId = (Reference2IntMap) toIdField.get(registry); - var byValue = (Map>) byValueField.get(registry); - var lifecycles = (Map) lifecyclesField.get(registry); + valueField.set(holder, value); + toId.put(value, toId.removeInt(oldValue)); + byValue.put(value, byValue.remove(oldValue)); + lifecycles.put(value, lifecycles.remove(oldValue)); + return true; + } catch (Throwable e) { + throw new IllegalStateException(e); + } + } - var newValue = DimensionType.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file))) - .get().left().map(Pair::getFirst).map(Holder::value).orElse(null); - if (newValue == null) - throw new IllegalArgumentException("Failed to parse dimension type " + key.location() + " from " + file); - - valueField.set(holder, newValue); - toId.put(newValue, toId.removeInt(oldValue)); - byValue.put(newValue, byValue.remove(oldValue)); - lifecycles.put(newValue, lifecycles.remove(oldValue)); + private MappedRegistry registry(ResourceKey> registryKey) { + var rawRegistry = registry().registry(registryKey).orElse(null); + if (!(rawRegistry instanceof MappedRegistry registry)) + throw new IllegalStateException("The Registry is not a mapped Registry!"); + return registry; } private static String buildType(Class clazz, String... parameterTypes) { @@ -719,6 +762,13 @@ public class NMSBinding implements INMSBinding { .visit(Advice.to(WorldCreatorAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(String.class)))) .make() .load(WorldCreator.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); + new ByteBuddy() + .redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, + PrimaryLevelData.class, ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, + List.class, boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); Iris.info("Injected Bukkit Successfully!"); } catch (Exception e) { Iris.info(C.RED + "Failed to Inject Bukkit!"); @@ -728,6 +778,28 @@ public class NMSBinding implements INMSBinding { } + private static class ServerLevelAdvice { + @Advice.OnMethodEnter + static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { + File iris = new File(access.levelDirectory.path().toFile(), "iris"); + if (!iris.exists()) return; + var logger = MinecraftServer.LOGGER; + ResourceKey typeKey = ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", key.location().getPath())); + RegistryAccess registryAccess = server.registryAccess(); + Registry registry = registryAccess.registry(Registries.DIMENSION_TYPE).orElse(null); + if (registry == null) { + logger.warn("Unable to find registry for dimension type {}", typeKey); + return; + } + Holder holder = registry.getHolder(typeKey).orElse(null); + if (holder == null) { + logger.warn("Unable to find dimension type {}", typeKey); + return; + } + levelStem = new LevelStem(holder, levelStem.generator()); + } + } + private static class WorldCreatorAdvice { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) String name) { 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 2e9720472..724b52dc1 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 @@ -13,13 +13,19 @@ import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Vector; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import com.google.common.base.Preconditions; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import com.mojang.serialization.Lifecycle; +import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.format.C; import com.volmit.iris.util.io.IO; import it.unimi.dsi.fastutil.objects.Reference2IntMap; @@ -28,9 +34,15 @@ import net.bytebuddy.asm.Advice; import net.bytebuddy.dynamic.loading.ClassReloadingStrategy; import net.bytebuddy.matcher.ElementMatchers; import net.minecraft.core.MappedRegistry; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.util.GsonHelper; +import net.minecraft.world.RandomSequences; import net.minecraft.world.level.Level; import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -44,6 +56,7 @@ import org.bukkit.craftbukkit.v1_20_R2.util.CraftNamespacedKey; import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -553,25 +566,34 @@ public class NMSBinding implements INMSBinding { } @Override - public boolean loadDatapack(File folder) { + public boolean registerDimension(String name, IrisDimension dimension) { + var registry = registry(Registries.DIMENSION_TYPE); + var baseLocation = switch (dimension.getEnvironment()) { + case NORMAL -> new ResourceLocation("minecraft", "overworld"); + case NETHER -> new ResourceLocation("minecraft", "the_nether"); + case THE_END -> new ResourceLocation("minecraft", "the_end"); + case CUSTOM -> throw new IllegalArgumentException("Cannot register custom dimension"); + }; + var base = registry.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, baseLocation)).orElse(null); + if (base == null) return false; + var json = encode(DimensionType.CODEC, base).orElse(null); + if (json == null) return false; + var object = json.getAsJsonObject(); + var height = dimension.getDimensionHeight(); + object.addProperty("min_y", height.getMin()); + object.addProperty("height", height.getMax() - height.getMin()); + object.addProperty("logical_height", dimension.getLogicalHeight()); + var value = decode(DimensionType.CODEC, object.toString()).map(Holder::value).orElse(null); + if (value == null) return false; + return register(Registries.DIMENSION_TYPE, new ResourceLocation("iris", name), value, true); + } + + @Override + public boolean loadDatapack(File folder, boolean replace) { var data = new File(folder, "iris/data"); if (!data.exists() || !data.isDirectory()) return false; FilenameFilter jsonFilter = (dir, name) -> new File(dir, name).isFile() && name.toLowerCase().endsWith(".json"); - var dimensionFolder = new File(data, "minecraft/dimension_type"); - if (dimensionFolder.exists()) { - var files = dimensionFolder.listFiles(jsonFilter); - if (files != null) { - for (File file : files) { - try { - modifyDimension(file); - } catch (Throwable e) { - Iris.error("Unable to modify dimension!"); - e.printStackTrace(); - } - } - } - } var files = data.listFiles((dir, name) -> new File(dir, name).isDirectory()); if (files == null) return false; for (File file : files) { @@ -581,7 +603,8 @@ public class NMSBinding implements INMSBinding { if (biomeFiles == null) continue; for (File biomeFile : biomeFiles) { try { - registerBiome(file.getName(), biomeFile); + var value = decode(net.minecraft.world.level.biome.Biome.CODEC, IO.readAll(file)).map(Holder::value).orElse(null); + register(Registries.BIOME, from(file.getName(), biomeFile), value, replace); } catch (Throwable e) { Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName()); e.printStackTrace(); @@ -596,75 +619,95 @@ public class NMSBinding implements INMSBinding { return new ResourceLocation(namespace, name.substring(0, name.lastIndexOf('.'))); } - private void registerBiome(String namespace, File file) throws Throwable { - var rawRegistry = registry().registry(Registries.BIOME).orElse(null); - var key = ResourceKey.create(Registries.BIOME, from(namespace, file)); - if (!(rawRegistry instanceof MappedRegistry registry)) - throw new IllegalStateException("The Biome Registry is not a mapped Registry!"); - if (registry.containsKey(key)) return; - Field field = getField(MappedRegistry.class, boolean.class); - field.setAccessible(true); - boolean frozen = field.getBoolean(registry); - field.setBoolean(registry, false); - Field holdersField = null; - boolean holders = false; - for (Field f : MappedRegistry.class.getDeclaredFields()) { - if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); - } + private Optional decode(Codec codec, String json) { + return codec.decode(JsonOps.INSTANCE, GsonHelper.parse(json)).get().left().map(Pair::getFirst); + } + private Optional encode(Codec codec, T value) { + return codec.encode(value, JsonOps.INSTANCE, new JsonObject()).result(); + } + + private boolean register(ResourceKey> registryKey, ResourceLocation location, T value, boolean replace) { + Preconditions.checkArgument(registryKey != null, "The registry cannot be null!"); + Preconditions.checkArgument(location != null, "The location cannot be null!"); + Preconditions.checkArgument(value != null, "The value cannot be null!"); + var registry = registry(registryKey); + var key = ResourceKey.create(registryKey, location); try { - var biome = net.minecraft.world.level.biome.Biome.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file))) - .get().left().map(Pair::getFirst).map(Holder::value).orElse(null); - if (biome == null) - throw new IllegalStateException("Failed to decode biome " + file.getName()); - - registry.createIntrusiveHolder(biome); - registry.register(key, biome, Lifecycle.stable()); - } finally { - field.setBoolean(registry, frozen); - if (holders) { - holdersField.set(registry, null); + if (registry.containsKey(key)) { + if (!replace) return false; + return replace(registryKey, location, value); } + Field field = getField(MappedRegistry.class, boolean.class); + field.setAccessible(true); + boolean frozen = field.getBoolean(registry); + field.setBoolean(registry, false); + Field holdersField = null; + boolean holders = false; + for (Field f : MappedRegistry.class.getDeclaredFields()) { + if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); + } + + try { + registry.createIntrusiveHolder(value); + registry.register(key, value, Lifecycle.stable()); + return true; + } finally { + field.setBoolean(registry, frozen); + if (holders) { + holdersField.set(registry, null); + } + } + } catch (Throwable e) { + throw new IllegalStateException(e); } } @SuppressWarnings("unchecked") - private void modifyDimension(File file) throws Throwable { - var key = ResourceKey.create(Registries.DIMENSION_TYPE, from("minecraft", file)); - var rawRegistry = registry().registry(Registries.DIMENSION_TYPE).orElse(null); - if (!(rawRegistry instanceof MappedRegistry registry)) - throw new IllegalStateException("The Dimension Registry is not a mapped Registry!"); + private boolean replace(ResourceKey> registryKey, ResourceLocation location, T value) { + Preconditions.checkArgument(registryKey != null, "The registryKey cannot be null!"); + Preconditions.checkArgument(location != null, "The location cannot be null!"); + Preconditions.checkArgument(value != null, "The value cannot be null!"); + var registry = registry(registryKey); + var key = ResourceKey.create(registryKey, location); + try { + var holder = registry.getHolder(key).orElse(null); + if (holder == null) return false; + var oldValue = holder.value(); + Field valueField = getField(Holder.Reference.class, "T"); + valueField.setAccessible(true); + Field toIdField = getField(MappedRegistry.class, buildType(Reference2IntMap.class, "T")); + toIdField.setAccessible(true); + Field byValueField = getField(MappedRegistry.class, buildType(Map.class, "T", buildType(Holder.Reference.class, "T"))); + byValueField.setAccessible(true); + Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName())); + lifecyclesField.setAccessible(true); + var toId = (Reference2IntMap) toIdField.get(registry); + var byValue = (Map>) byValueField.get(registry); + var lifecycles = (Map) lifecyclesField.get(registry); - var holder = registry.getHolder(key).orElseThrow(() -> new IllegalStateException("Unknown dimension type: " + key)); - var oldValue = holder.value(); - Field valueField = getField(Holder.Reference.class, "T"); - valueField.setAccessible(true); - Field toIdField = getField(MappedRegistry.class, buildType(Reference2IntMap.class, "T")); - toIdField.setAccessible(true); - Field byValueField = getField(MappedRegistry.class, buildType(Map.class, "T", buildType(Holder.Reference.class, "T"))); - byValueField.setAccessible(true); - Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName())); - lifecyclesField.setAccessible(true); - var toId = (Reference2IntMap) toIdField.get(registry); - var byValue = (Map>) byValueField.get(registry); - var lifecycles = (Map) lifecyclesField.get(registry); + valueField.set(holder, value); + toId.put(value, toId.removeInt(oldValue)); + byValue.put(value, byValue.remove(oldValue)); + lifecycles.put(value, lifecycles.remove(oldValue)); + return true; + } catch (Throwable e) { + throw new IllegalStateException(e); + } + } - var newValue = DimensionType.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file))) - .get().left().map(Pair::getFirst).map(Holder::value).orElse(null); - if (newValue == null) - throw new IllegalArgumentException("Failed to parse dimension type " + key.location() + " from " + file); - - valueField.set(holder, newValue); - toId.put(newValue, toId.removeInt(oldValue)); - byValue.put(newValue, byValue.remove(oldValue)); - lifecycles.put(newValue, lifecycles.remove(oldValue)); + private MappedRegistry registry(ResourceKey> registryKey) { + var rawRegistry = registry().registry(registryKey).orElse(null); + if (!(rawRegistry instanceof MappedRegistry registry)) + throw new IllegalStateException("The Registry is not a mapped Registry!"); + return registry; } private static String buildType(Class clazz, String... parameterTypes) { @@ -721,6 +764,13 @@ public class NMSBinding implements INMSBinding { .visit(Advice.to(WorldCreatorAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(String.class)))) .make() .load(WorldCreator.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); + new ByteBuddy() + .redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, + PrimaryLevelData.class, ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, + List.class, boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); Iris.info("Injected Bukkit Successfully!"); } catch (Exception e) { Iris.info(C.RED + "Failed to Inject Bukkit!"); @@ -730,6 +780,28 @@ public class NMSBinding implements INMSBinding { } + private static class ServerLevelAdvice { + @Advice.OnMethodEnter + static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { + File iris = new File(access.levelDirectory.path().toFile(), "iris"); + if (!iris.exists()) return; + var logger = MinecraftServer.LOGGER; + ResourceKey typeKey = ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", key.location().getPath())); + RegistryAccess registryAccess = server.registryAccess(); + Registry registry = registryAccess.registry(Registries.DIMENSION_TYPE).orElse(null); + if (registry == null) { + logger.warn("Unable to find registry for dimension type {}", typeKey); + return; + } + Holder holder = registry.getHolder(typeKey).orElse(null); + if (holder == null) { + logger.warn("Unable to find dimension type {}", typeKey); + return; + } + levelStem = new LevelStem(holder, levelStem.generator()); + } + } + private static class WorldCreatorAdvice { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) String name) { 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 14403b714..cb67298e0 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 @@ -6,27 +6,54 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FilenameFilter; +import java.io.IOException; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.nio.file.Files; import java.util.*; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Logger; import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import com.mojang.serialization.Lifecycle; +import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.format.C; +import com.volmit.iris.util.function.NastySupplier; import com.volmit.iris.util.io.IO; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import net.bytebuddy.ByteBuddy; +import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.loading.ClassReloadingStrategy; import net.bytebuddy.matcher.ElementMatchers; +import net.bytebuddy.utility.JavaModule; +import net.minecraft.core.IdMapper; import net.minecraft.core.MappedRegistry; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.util.GsonHelper; +import net.minecraft.util.worldupdate.WorldUpgrader; +import net.minecraft.world.RandomSequences; import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -40,6 +67,7 @@ import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey; import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -445,13 +473,13 @@ public class NMSBinding implements INMSBinding { @Override public MCAPaletteAccess createPalette() { MCAIdMapper registry = registryCache.aquireNasty(() -> { - Field cf = net.minecraft.core.IdMapper.class.getDeclaredField("tToId"); - Field df = net.minecraft.core.IdMapper.class.getDeclaredField("idToT"); - Field bf = net.minecraft.core.IdMapper.class.getDeclaredField("nextId"); + Field cf = IdMapper.class.getDeclaredField("tToId"); + Field df = IdMapper.class.getDeclaredField("idToT"); + Field bf = IdMapper.class.getDeclaredField("nextId"); cf.setAccessible(true); df.setAccessible(true); bf.setAccessible(true); - net.minecraft.core.IdMapper blockData = Block.BLOCK_STATE_REGISTRY; + IdMapper blockData = Block.BLOCK_STATE_REGISTRY; int b = bf.getInt(blockData); Object2IntMap c = (Object2IntMap) cf.get(blockData); List d = (List) df.get(blockData); @@ -551,25 +579,34 @@ public class NMSBinding implements INMSBinding { } @Override - public boolean loadDatapack(File folder) { + public boolean registerDimension(String name, IrisDimension dimension) { + var registry = registry(Registries.DIMENSION_TYPE); + var baseLocation = switch (dimension.getEnvironment()) { + case NORMAL -> new ResourceLocation("minecraft", "overworld"); + case NETHER -> new ResourceLocation("minecraft", "the_nether"); + case THE_END -> new ResourceLocation("minecraft", "the_end"); + case CUSTOM -> throw new IllegalArgumentException("Cannot register custom dimension"); + }; + var base = registry.getHolder(ResourceKey.create(Registries.DIMENSION_TYPE, baseLocation)).orElse(null); + if (base == null) return false; + var json = encode(DimensionType.CODEC, base).orElse(null); + if (json == null) return false; + var object = json.getAsJsonObject(); + var height = dimension.getDimensionHeight(); + object.addProperty("min_y", height.getMin()); + object.addProperty("height", height.getMax() - height.getMin()); + object.addProperty("logical_height", dimension.getLogicalHeight()); + var value = decode(DimensionType.CODEC, object.toString()).map(Holder::value).orElse(null); + if (value == null) return false; + return register(Registries.DIMENSION_TYPE, new ResourceLocation("iris", name), value, true); + } + + @Override + public boolean loadDatapack(File folder, boolean replace) { var data = new File(folder, "iris/data"); if (!data.exists() || !data.isDirectory()) return false; FilenameFilter jsonFilter = (dir, name) -> new File(dir, name).isFile() && name.toLowerCase().endsWith(".json"); - var dimensionFolder = new File(data, "minecraft/dimension_type"); - if (dimensionFolder.exists()) { - var files = dimensionFolder.listFiles(jsonFilter); - if (files != null) { - for (File file : files) { - try { - modifyDimension(file); - } catch (Throwable e) { - Iris.error("Unable to modify dimension!"); - e.printStackTrace(); - } - } - } - } var files = data.listFiles((dir, name) -> new File(dir, name).isDirectory()); if (files == null) return false; for (File file : files) { @@ -579,7 +616,8 @@ public class NMSBinding implements INMSBinding { if (biomeFiles == null) continue; for (File biomeFile : biomeFiles) { try { - registerBiome(file.getName(), biomeFile); + var value = decode(net.minecraft.world.level.biome.Biome.CODEC, IO.readAll(file)).map(Holder::value).orElse(null); + register(Registries.BIOME, from(file.getName(), biomeFile), value, replace); } catch (Throwable e) { Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName()); e.printStackTrace(); @@ -594,75 +632,95 @@ public class NMSBinding implements INMSBinding { return new ResourceLocation(namespace, name.substring(0, name.lastIndexOf('.'))); } - private void registerBiome(String namespace, File file) throws Throwable { - var rawRegistry = registry().registry(Registries.BIOME).orElse(null); - var key = ResourceKey.create(Registries.BIOME, from(namespace, file)); - if (!(rawRegistry instanceof MappedRegistry registry)) - throw new IllegalStateException("The Biome Registry is not a mapped Registry!"); - if (registry.containsKey(key)) return; - Field field = getField(MappedRegistry.class, boolean.class); - field.setAccessible(true); - boolean frozen = field.getBoolean(registry); - field.setBoolean(registry, false); - Field holdersField = null; - boolean holders = false; - for (Field f : MappedRegistry.class.getDeclaredFields()) { - if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); - } + private Optional decode(Codec codec, String json) { + return codec.decode(JsonOps.INSTANCE, GsonHelper.parse(json)).get().left().map(Pair::getFirst); + } + private Optional encode(Codec codec, T value) { + return codec.encode(value, JsonOps.INSTANCE, new JsonObject()).result(); + } + + private boolean register(ResourceKey> registryKey, ResourceLocation location, T value, boolean replace) { + Preconditions.checkArgument(registryKey != null, "The registry cannot be null!"); + Preconditions.checkArgument(location != null, "The location cannot be null!"); + Preconditions.checkArgument(value != null, "The value cannot be null!"); + var registry = registry(registryKey); + var key = ResourceKey.create(registryKey, location); try { - var biome = net.minecraft.world.level.biome.Biome.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file))) - .get().left().map(Pair::getFirst).map(Holder::value).orElse(null); - if (biome == null) - throw new IllegalStateException("Failed to decode biome " + file.getName()); - - registry.createIntrusiveHolder(biome); - registry.register(key, biome, Lifecycle.stable()); - } finally { - field.setBoolean(registry, frozen); - if (holders) { - holdersField.set(registry, null); + if (registry.containsKey(key)) { + if (!replace) return false; + return replace(registryKey, location, value); } + Field field = getField(MappedRegistry.class, boolean.class); + field.setAccessible(true); + boolean frozen = field.getBoolean(registry); + field.setBoolean(registry, false); + Field holdersField = null; + boolean holders = false; + for (Field f : MappedRegistry.class.getDeclaredFields()) { + if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); + } + + try { + registry.createIntrusiveHolder(value); + registry.register(key, value, Lifecycle.stable()); + return true; + } finally { + field.setBoolean(registry, frozen); + if (holders) { + holdersField.set(registry, null); + } + } + } catch (Throwable e) { + throw new IllegalStateException(e); } } @SuppressWarnings("unchecked") - private void modifyDimension(File file) throws Throwable { - var key = ResourceKey.create(Registries.DIMENSION_TYPE, from("minecraft", file)); - var rawRegistry = registry().registry(Registries.DIMENSION_TYPE).orElse(null); - if (!(rawRegistry instanceof MappedRegistry registry)) - throw new IllegalStateException("The Dimension Registry is not a mapped Registry!"); + private boolean replace(ResourceKey> registryKey, ResourceLocation location, T value) { + Preconditions.checkArgument(registryKey != null, "The registryKey cannot be null!"); + Preconditions.checkArgument(location != null, "The location cannot be null!"); + Preconditions.checkArgument(value != null, "The value cannot be null!"); + var registry = registry(registryKey); + var key = ResourceKey.create(registryKey, location); + try { + var holder = registry.getHolder(key).orElse(null); + if (holder == null) return false; + var oldValue = holder.value(); + Field valueField = getField(Holder.Reference.class, "T"); + valueField.setAccessible(true); + Field toIdField = getField(MappedRegistry.class, buildType(Reference2IntMap.class, "T")); + toIdField.setAccessible(true); + Field byValueField = getField(MappedRegistry.class, buildType(Map.class, "T", buildType(Holder.Reference.class, "T"))); + byValueField.setAccessible(true); + Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName())); + lifecyclesField.setAccessible(true); + var toId = (Reference2IntMap) toIdField.get(registry); + var byValue = (Map>) byValueField.get(registry); + var lifecycles = (Map) lifecyclesField.get(registry); - var holder = registry.getHolder(key).orElseThrow(() -> new IllegalStateException("Unknown dimension type: " + key)); - var oldValue = holder.value(); - Field valueField = getField(Holder.Reference.class, "T"); - valueField.setAccessible(true); - Field toIdField = getField(MappedRegistry.class, buildType(Reference2IntMap.class, "T")); - toIdField.setAccessible(true); - Field byValueField = getField(MappedRegistry.class, buildType(Map.class, "T", buildType(Holder.Reference.class, "T"))); - byValueField.setAccessible(true); - Field lifecyclesField = getField(MappedRegistry.class, buildType(Map.class, "T", Lifecycle.class.getName())); - lifecyclesField.setAccessible(true); - var toId = (Reference2IntMap) toIdField.get(registry); - var byValue = (Map>) byValueField.get(registry); - var lifecycles = (Map) lifecyclesField.get(registry); + valueField.set(holder, value); + toId.put(value, toId.removeInt(oldValue)); + byValue.put(value, byValue.remove(oldValue)); + lifecycles.put(value, lifecycles.remove(oldValue)); + return true; + } catch (Throwable e) { + throw new IllegalStateException(e); + } + } - var newValue = DimensionType.CODEC.decode(JsonOps.INSTANCE, GsonHelper.parse(IO.readAll(file))) - .get().left().map(Pair::getFirst).map(Holder::value).orElse(null); - if (newValue == null) - throw new IllegalArgumentException("Failed to parse dimension type " + key.location() + " from " + file); - - valueField.set(holder, newValue); - toId.put(newValue, toId.removeInt(oldValue)); - byValue.put(newValue, byValue.remove(oldValue)); - lifecycles.put(newValue, lifecycles.remove(oldValue)); + private MappedRegistry registry(ResourceKey> registryKey) { + var rawRegistry = registry().registry(registryKey).orElse(null); + if (!(rawRegistry instanceof MappedRegistry registry)) + throw new IllegalStateException("The Registry is not a mapped Registry!"); + return registry; } private static String buildType(Class clazz, String... parameterTypes) { @@ -718,6 +776,13 @@ public class NMSBinding implements INMSBinding { .visit(Advice.to(WorldCreatorAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(String.class)))) .make() .load(WorldCreator.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); + new ByteBuddy() + .redefine(ServerLevel.class) + .visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(MinecraftServer.class, Executor.class, LevelStorageSource.LevelStorageAccess.class, + PrimaryLevelData.class, ResourceKey.class, LevelStem.class, ChunkProgressListener.class, boolean.class, long.class, + List.class, boolean.class, RandomSequences.class, World.Environment.class, ChunkGenerator.class, BiomeProvider.class)))) + .make() + .load(ServerLevel.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); Iris.info("Injected Bukkit Successfully!"); } catch (Exception e) { Iris.info(C.RED + "Failed to Inject Bukkit!"); @@ -727,6 +792,28 @@ public class NMSBinding implements INMSBinding { } + private static class ServerLevelAdvice { + @Advice.OnMethodEnter + static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { + File iris = new File(access.levelDirectory.path().toFile(), "iris"); + if (!iris.exists()) return; + var logger = MinecraftServer.LOGGER; + ResourceKey typeKey = ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", key.location().getPath())); + RegistryAccess registryAccess = server.registryAccess(); + Registry registry = registryAccess.registry(Registries.DIMENSION_TYPE).orElse(null); + if (registry == null) { + logger.warn("Unable to find registry for dimension type {}", typeKey); + return; + } + Holder holder = registry.getHolder(typeKey).orElse(null); + if (holder == null) { + logger.warn("Unable to find dimension type {}", typeKey); + return; + } + levelStem = new LevelStem(holder, levelStem.generator()); + } + } + private static class WorldCreatorAdvice { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) String name) { From 8a8be4545cd82fdb15d11dc93b910a53bbc12472 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 3 May 2024 19:11:55 +0200 Subject: [PATCH 04/10] remove unnecessary minecraft dimension height calculations --- .../volmit/iris/core/ServerConfigurator.java | 26 +--- .../iris/engine/object/IrisDimension.java | 147 +----------------- 2 files changed, 6 insertions(+), 167 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java index c4d20f73e..f3dafea22 100644 --- a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java +++ b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java @@ -135,30 +135,6 @@ public class ServerConfigurator { public static void installDataPacks(boolean fullInstall) { Iris.info("Checking Data Packs..."); File packs = new File("plugins/Iris/packs"); - double ultimateMaxHeight = 0; - double ultimateMinHeight = 0; - if (packs.exists() && packs.isDirectory()) { - for (File pack : packs.listFiles()) { - IrisData data = IrisData.get(pack); - if (pack.isDirectory()) { - File dimensionsFolder = new File(pack, "dimensions"); - if (dimensionsFolder.exists() && dimensionsFolder.isDirectory()) { - for (File file : dimensionsFolder.listFiles()) { - if (file.isFile() && file.getName().endsWith(".json")) { - IrisDimension dim = data.getDimensionLoader().load(file.getName().split("\\Q.\\E")[0]); - if (ultimateMaxHeight < dim.getDimensionHeight().getMax()) { - ultimateMaxHeight = dim.getDimensionHeight().getMax(); - } - if (ultimateMinHeight > dim.getDimensionHeight().getMin()) { - ultimateMinHeight = dim.getDimensionHeight().getMin(); - } - } - } - } - } - } - } - if (packs.exists()) { for (File i : packs.listFiles()) { if (i.isDirectory()) { @@ -177,7 +153,7 @@ public class ServerConfigurator { Iris.verbose(" Checking Dimension " + dim.getLoadFile().getPath()); for (File dpack : getDatapacksFolder()) { - dim.installDataPack(() -> data, dpack, ultimateMaxHeight, ultimateMinHeight); + dim.installDataPack(() -> data, dpack); } } } diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java index aee21600b..bedc18282 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java @@ -56,73 +56,6 @@ import java.io.IOException; public class IrisDimension extends IrisRegistrant { public static final BlockData STONE = Material.STONE.createBlockData(); public static final BlockData WATER = Material.WATER.createBlockData(); - private static final String DP_OVERWORLD_DEFAULT = """ - { - "ambient_light": 0.0, - "bed_works": true, - "coordinate_scale": 1.0, - "effects": "minecraft:overworld", - "has_ceiling": false, - "has_raids": true, - "has_skylight": true, - "infiniburn": "#minecraft:infiniburn_overworld", - "monster_spawn_block_light_limit": 0, - "monster_spawn_light_level": { - "type": "minecraft:uniform", - "value": { - "max_inclusive": 7, - "min_inclusive": 0 - } - }, - "natural": true, - "piglin_safe": false, - "respawn_anchor_works": false, - "ultrawarm": false - }"""; - - private static final String DP_NETHER_DEFAULT = """ - { - "ambient_light": 0.1, - "bed_works": false, - "coordinate_scale": 8.0, - "effects": "minecraft:the_nether", - "fixed_time": 18000, - "has_ceiling": true, - "has_raids": false, - "has_skylight": false, - "infiniburn": "#minecraft:infiniburn_nether", - "monster_spawn_block_light_limit": 15, - "monster_spawn_light_level": 7, - "natural": false, - "piglin_safe": true, - "respawn_anchor_works": true, - "ultrawarm": true - }"""; - - private static final String DP_END_DEFAULT = """ - { - "ambient_light": 0.0, - "bed_works": false, - "coordinate_scale": 1.0, - "effects": "minecraft:the_end", - "fixed_time": 6000, - "has_ceiling": false, - "has_raids": true, - "has_skylight": false, - "infiniburn": "#minecraft:infiniburn_end", - "monster_spawn_block_light_limit": 0, - "monster_spawn_light_level": { - "type": "minecraft:uniform", - "value": { - "max_inclusive": 7, - "min_inclusive": 0 - } - }, - "natural": false, - "piglin_safe": false, - "respawn_anchor_works": false, - "ultrawarm": false - }"""; private final transient AtomicCache parallaxSize = new AtomicCache<>(); private final transient AtomicCache rockLayerGenerator = new AtomicCache<>(); private final transient AtomicCache fluidLayerGenerator = new AtomicCache<>(); @@ -230,10 +163,6 @@ public class IrisDimension extends IrisRegistrant { private int fluidHeight = 63; @Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.") private IrisRange dimensionHeight = new IrisRange(-64, 320); - @Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.") - private IrisRange dimensionHeightEnd = new IrisRange(-64, 320); - @Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.") - private IrisRange dimensionHeightNether = new IrisRange(-64, 320); @Desc("Enable smart vanilla height") private boolean smartVanillaHeight = false; @RegistryListResource(IrisBiome.class) @@ -376,6 +305,10 @@ public class IrisDimension extends IrisRegistrant { return environment; } + public IrisRange getDimensionHeight() { + return smartVanillaHeight ? new IrisRange(-64, 320) : dimensionHeight; + } + public boolean hasFocusRegion() { return !focusRegion.equals(""); } @@ -447,7 +380,7 @@ public class IrisDimension extends IrisRegistrant { return landBiomeStyle; } - public boolean installDataPack(DataProvider data, File datapacks, double ultimateMaxHeight, double ultimateMinHeight) { + public boolean installDataPack(DataProvider data, File datapacks) { boolean write = false; boolean changed = false; @@ -476,13 +409,6 @@ public class IrisDimension extends IrisRegistrant { } } - if (!dimensionHeight.equals(new IrisRange(-64, 320)) && this.name.equalsIgnoreCase("overworld")) { - Iris.verbose(" Installing Data Pack Dimension Types: \"minecraft:overworld\", \"minecraft:the_nether\", \"minecraft:the_end\""); - dimensionHeight.setMax(ultimateMaxHeight); - dimensionHeight.setMin(ultimateMinHeight); - changed = writeDimensionType(changed, datapacks); - } - if (write) { File mcm = new File(datapacks, "iris/pack.mcmeta"); try { @@ -518,67 +444,4 @@ public class IrisDimension extends IrisRegistrant { public void scanForErrors(JSONObject p, VolmitSender sender) { } - - public boolean writeDimensionType(boolean changed, File datapacks) { - File dimTypeOverworld = new File(datapacks, "iris/data/minecraft/dimension_type/overworld.json"); - if (!dimTypeOverworld.exists()) - changed = true; - dimTypeOverworld.getParentFile().mkdirs(); - try { - IO.writeAll(dimTypeOverworld, generateDatapackJsonOverworld()); - } catch (IOException e) { - Iris.reportError(e); - e.printStackTrace(); - } - - - File dimTypeNether = new File(datapacks, "iris/data/minecraft/dimension_type/the_nether.json"); - if (!dimTypeNether.exists()) - changed = true; - dimTypeNether.getParentFile().mkdirs(); - try { - IO.writeAll(dimTypeNether, generateDatapackJsonNether()); - } catch (IOException e) { - Iris.reportError(e); - e.printStackTrace(); - } - - - File dimTypeEnd = new File(datapacks, "iris/data/minecraft/dimension_type/the_end.json"); - if (!dimTypeEnd.exists()) - changed = true; - dimTypeEnd.getParentFile().mkdirs(); - try { - IO.writeAll(dimTypeEnd, generateDatapackJsonEnd()); - } catch (IOException e) { - Iris.reportError(e); - e.printStackTrace(); - } - - return changed; - } - - private String generateDatapackJsonOverworld() { - JSONObject obj = new JSONObject(DP_OVERWORLD_DEFAULT); - obj.put("min_y", dimensionHeight.getMin()); - obj.put("height", dimensionHeight.getMax() - dimensionHeight.getMin()); - obj.put("logical_height", logicalHeight); - return obj.toString(4); - } - - private String generateDatapackJsonNether() { - JSONObject obj = new JSONObject(DP_NETHER_DEFAULT); - obj.put("min_y", dimensionHeightNether.getMin()); - obj.put("height", dimensionHeightNether.getMax() - dimensionHeightNether.getMin()); - obj.put("logical_height", logicalHeightNether); - return obj.toString(4); - } - - private String generateDatapackJsonEnd() { - JSONObject obj = new JSONObject(DP_END_DEFAULT); - obj.put("min_y", dimensionHeightEnd.getMin()); - obj.put("height", dimensionHeightEnd.getMax() - dimensionHeightEnd.getMin()); - obj.put("logical_height", logicalHeightEnd); - return obj.toString(4); - } } From 6e247597a463b02a2f667d123a898ede832b811f Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 3 May 2024 19:55:58 +0200 Subject: [PATCH 05/10] fix loading the wrong IrisDimension on startup --- core/src/main/java/com/volmit/iris/Iris.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 213d96709..f916d9501 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -728,6 +728,11 @@ public class Iris extends VolmitPlugin implements Listener { Iris.info("Resolved missing dimension, proceeding with generation."); } } + File packFolder = new File(Bukkit.getWorldContainer(), worldName + "/iris/pack"); + if (packFolder.exists()) { + IrisDimension worldDim = IrisData.get(packFolder).getDimensionLoader().load(id); + if (worldDim != null) dim = worldDim; + } Iris.debug("Assuming IrisDimension: " + dim.getName()); From d4f9a20379a9c201257bd7f0e7c90155184f93e7 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 3 May 2024 19:56:43 +0200 Subject: [PATCH 06/10] more removal of unnecessary settings --- .../iris/engine/object/IrisDimension.java | 890 +++++++++--------- 1 file changed, 443 insertions(+), 447 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java index bedc18282..2837ff676 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java @@ -1,447 +1,443 @@ -/* - * 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.engine.object; - -import com.volmit.iris.Iris; -import com.volmit.iris.core.IrisSettings; -import com.volmit.iris.core.loader.IrisData; -import com.volmit.iris.core.loader.IrisRegistrant; -import com.volmit.iris.engine.data.cache.AtomicCache; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.object.annotations.*; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.data.DataProvider; -import com.volmit.iris.util.data.Dimension; -import com.volmit.iris.util.io.IO; -import com.volmit.iris.util.json.JSONObject; -import com.volmit.iris.util.math.Position2; -import com.volmit.iris.util.math.RNG; -import com.volmit.iris.util.noise.CNG; -import com.volmit.iris.util.plugin.VolmitSender; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.experimental.Accessors; -import org.bukkit.Material; -import org.bukkit.World.Environment; -import org.bukkit.block.data.BlockData; - -import java.io.DataInput; -import java.io.File; -import java.io.IOException; - -@Accessors(chain = true) -@AllArgsConstructor -@NoArgsConstructor -@Desc("Represents a dimension") -@Data -@EqualsAndHashCode(callSuper = false) -public class IrisDimension extends IrisRegistrant { - public static final BlockData STONE = Material.STONE.createBlockData(); - public static final BlockData WATER = Material.WATER.createBlockData(); - private final transient AtomicCache parallaxSize = new AtomicCache<>(); - private final transient AtomicCache rockLayerGenerator = new AtomicCache<>(); - private final transient AtomicCache fluidLayerGenerator = new AtomicCache<>(); - private final transient AtomicCache coordFracture = new AtomicCache<>(); - private final transient AtomicCache sinr = new AtomicCache<>(); - private final transient AtomicCache cosr = new AtomicCache<>(); - private final transient AtomicCache rad = new AtomicCache<>(); - private final transient AtomicCache featuresUsed = new AtomicCache<>(); - private final transient AtomicCache> strongholdsCache = new AtomicCache<>(); - @MinNumber(2) - @Required - @Desc("The human readable name of this dimension") - private String name = "A Dimension"; - @MinNumber(1) - @MaxNumber(2032) - @Desc("Maximum height at which players can be teleported to through gameplay.") - private int logicalHeight = 256; - @Desc("Maximum height at which players can be teleported to through gameplay.") - private int logicalHeightEnd = 256; - @Desc("Maximum height at which players can be teleported to through gameplay.") - private int logicalHeightNether = 256; - @RegistryListResource(IrisJigsawStructure.class) - @Desc("If defined, Iris will place the given jigsaw structure where minecraft should place the overworld stronghold.") - private String stronghold; - @Desc("If set to true, Iris will remove chunks to allow visualizing cross sections of chunks easily") - private boolean debugChunkCrossSections = false; - @Desc("Vertically split up the biome palettes with 3 air blocks in between to visualize them") - private boolean explodeBiomePalettes = false; - @Desc("Studio Mode for testing different parts of the world") - private StudioMode studioMode = StudioMode.NORMAL; - @MinNumber(1) - @MaxNumber(16) - @Desc("Customize the palette height explosion") - private int explodeBiomePaletteSize = 3; - @MinNumber(2) - @MaxNumber(16) - @Desc("Every X/Z % debugCrossSectionsMod == 0 cuts the chunk") - private int debugCrossSectionsMod = 3; - @Desc("The average distance between strongholds") - private int strongholdJumpDistance = 1280; - @Desc("Define the maximum strongholds to place") - private int maxStrongholds = 14; - @Desc("Tree growth override settings") - private IrisTreeSettings treeSettings = new IrisTreeSettings(); - @Desc("Spawn Entities in this dimension over time. Iris will continually replenish these mobs just like vanilla does.") - @ArrayType(min = 1, type = String.class) - @RegistryListResource(IrisSpawner.class) - private KList entitySpawners = new KList<>(); - @Desc("Reference loot tables in this area") - private IrisLootReference loot = new IrisLootReference(); - @MinNumber(0) - @Desc("The version of this dimension. Changing this will stop users from accidentally upgrading (and breaking their worlds).") - private int version = 1; - @ArrayType(min = 1, type = IrisBlockDrops.class) - @Desc("Define custom block drops for this dimension") - private KList blockDrops = new KList<>(); - @Desc("Should bedrock be generated or not.") - private boolean bedrock = true; - @MinNumber(0) - @MaxNumber(1) - @Desc("The land chance. Up to 1.0 for total land or 0.0 for total sea") - private double landChance = 0.625; - @Desc("The placement style of regions") - private IrisGeneratorStyle regionStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style(); - @Desc("The placement style of land/sea") - private IrisGeneratorStyle continentalStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style(); - @Desc("The placement style of biomes") - private IrisGeneratorStyle landBiomeStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style(); - @Desc("The placement style of biomes") - private IrisGeneratorStyle shoreBiomeStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style(); - @Desc("The placement style of biomes") - private IrisGeneratorStyle seaBiomeStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style(); - @Desc("The placement style of biomes") - private IrisGeneratorStyle caveBiomeStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style(); - @Desc("Instead of filling objects with air, fills them with cobweb so you can see them") - private boolean debugSmartBore = false; - @Desc("Generate decorations or not") - private boolean decorate = true; - @Desc("Use post processing or not") - private boolean postProcessing = true; - @Desc("Add slabs in post processing") - private boolean postProcessingSlabs = true; - @Desc("Add painted walls in post processing") - private boolean postProcessingWalls = true; - @Desc("Carving configuration for the dimension") - private IrisCarving carving = new IrisCarving(); - @Desc("Configuration of fluid bodies such as rivers & lakes") - private IrisFluidBodies fluidBodies = new IrisFluidBodies(); - @Desc("forceConvertTo320Height") - private Boolean forceConvertTo320Height = false; - @Desc("The world environment") - private Environment environment = Environment.NORMAL; - @RegistryListResource(IrisRegion.class) - @Required - @ArrayType(min = 1, type = String.class) - @Desc("Define all of the regions to include in this dimension. Dimensions -> Regions -> Biomes -> Objects etc") - private KList regions = new KList<>(); - @ArrayType(min = 1, type = IrisJigsawStructurePlacement.class) - @Desc("Jigsaw structures") - private KList jigsawStructures = new KList<>(); - @Required - @MinNumber(0) - @MaxNumber(1024) - @Desc("The fluid height for this dimension") - private int fluidHeight = 63; - @Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.") - private IrisRange dimensionHeight = new IrisRange(-64, 320); - @Desc("Enable smart vanilla height") - private boolean smartVanillaHeight = false; - @RegistryListResource(IrisBiome.class) - @Desc("Keep this either undefined or empty. Setting any biome name into this will force iris to only generate the specified biome. Great for testing.") - private String focus = ""; - @RegistryListResource(IrisRegion.class) - @Desc("Keep this either undefined or empty. Setting any region name into this will force iris to only generate the specified region. Great for testing.") - private String focusRegion = ""; - @MinNumber(0.0001) - @MaxNumber(512) - @Desc("Zoom in or out the biome size. Higher = bigger biomes") - private double biomeZoom = 5D; - @MinNumber(0) - @MaxNumber(360) - @Desc("You can rotate the input coordinates by an angle. This can make terrain appear more natural (less sharp corners and lines). This literally rotates the entire dimension by an angle. Hint: Try 12 degrees or something not on a 90 or 45 degree angle.") - private double dimensionAngleDeg = 0; - @Required - @Desc("Define the mode of this dimension (required!)") - private IrisDimensionMode mode = new IrisDimensionMode(); - @MinNumber(0) - @MaxNumber(8192) - @Desc("Coordinate fracturing applies noise to the input coordinates. This creates the 'iris swirls' and wavy features. The distance pushes these waves further into places they shouldnt be. This is a block value multiplier.") - private double coordFractureDistance = 20; - @MinNumber(0.0001) - @MaxNumber(512) - @Desc("Coordinate fracturing zoom. Higher = less frequent warping, Lower = more frequent and rapid warping / swirls.") - private double coordFractureZoom = 8; - @MinNumber(0.0001) - @MaxNumber(512) - @Desc("This zooms in the land space") - private double landZoom = 1; - @MinNumber(0.0001) - @MaxNumber(512) - @Desc("This zooms oceanic biomes") - private double seaZoom = 1; - @MinNumber(0.0001) - @MaxNumber(512) - @Desc("Zoom in continents") - private double continentZoom = 1; - @MinNumber(0.0001) - @MaxNumber(512) - @Desc("Change the size of regions") - private double regionZoom = 1; - @Desc("Disable this to stop placing objects, entities, features & updates") - private boolean useMantle = true; - @Desc("Prevent Leaf decay as if placed in creative mode") - private boolean preventLeafDecay = false; - @ArrayType(min = 1, type = IrisDepositGenerator.class) - @Desc("Define global deposit generators") - private KList deposits = new KList<>(); - @ArrayType(min = 1, type = IrisShapedGeneratorStyle.class) - @Desc("Overlay additional noise on top of the interoplated terrain.") - private KList overlayNoise = new KList<>(); - @Desc("If true, the spawner system has infinite energy. This is NOT recommended because it would allow for mobs to keep spawning over and over without a rate limit") - private boolean infiniteEnergy = false; - @MinNumber(0) - @MaxNumber(10000) - @Desc("This is the maximum energy you can have in a dimension") - private double maximumEnergy = 1000; - @MinNumber(0.0001) - @MaxNumber(512) - @Desc("The rock zoom mostly for zooming in on a wispy palette") - private double rockZoom = 5; - @Desc("The palette of blocks for 'stone'") - private IrisMaterialPalette rockPalette = new IrisMaterialPalette().qclear().qadd("stone"); - @Desc("The palette of blocks for 'water'") - private IrisMaterialPalette fluidPalette = new IrisMaterialPalette().qclear().qadd("water"); - @Desc("Remove cartographers so they do not crash the server (Iris worlds only)") - private boolean removeCartographersDueToCrash = true; - @Desc("Notify players of cancelled cartographer villager in this radius in blocks (set to -1 to disable, -2 for everyone)") - private int notifyPlayersOfCartographerCancelledRadius = 30; - @Desc("Collection of ores to be generated") - @ArrayType(type = IrisOreGenerator.class, min = 1) - private KList ores = new KList<>(); - @MinNumber(0) - @MaxNumber(318) - @Desc("The Subterrain Fluid Layer Height") - private int caveLavaHeight = 8; - - public int getMaxHeight() { - return (int) getDimensionHeight().getMax(); - } - - public int getMinHeight() { - return (int) getDimensionHeight().getMin(); - } - - public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data) { - if (ores.isEmpty()) { - return null; - } - BlockData b = null; - for (IrisOreGenerator i : ores) { - - b = i.generate(x, y, z, rng, data); - if (b != null) { - return b; - } - } - return null; - } - - public KList getStrongholds(long seed) { - return strongholdsCache.aquire(() -> { - KList pos = new KList<>(); - int jump = strongholdJumpDistance; - RNG rng = new RNG((seed * 223) + 12945); - for (int i = 0; i < maxStrongholds + 1; i++) { - int m = i + 1; - pos.add(new Position2( - (int) ((rng.i(jump * i) + (jump * i)) * (rng.b() ? -1D : 1D)), - (int) ((rng.i(jump * i) + (jump * i)) * (rng.b() ? -1D : 1D)) - )); - } - - pos.remove(0); - - return pos; - }); - } - - public int getFluidHeight() { - return fluidHeight - (int) dimensionHeight.getMin(); - } - - public CNG getCoordFracture(RNG rng, int signature) { - return coordFracture.aquire(() -> - { - CNG coordFracture = CNG.signature(rng.nextParallelRNG(signature)); - coordFracture.scale(0.012 / coordFractureZoom); - return coordFracture; - }); - } - - public double getDimensionAngle() { - return rad.aquire(() -> Math.toRadians(dimensionAngleDeg)); - } - - public Environment getEnvironment() { - return environment; - } - - public IrisRange getDimensionHeight() { - return smartVanillaHeight ? new IrisRange(-64, 320) : dimensionHeight; - } - - public boolean hasFocusRegion() { - return !focusRegion.equals(""); - } - - public String getFocusRegion() { - return focusRegion; - } - - public double sinRotate() { - return sinr.aquire(() -> Math.sin(getDimensionAngle())); - } - - public double cosRotate() { - return cosr.aquire(() -> Math.cos(getDimensionAngle())); - } - - public KList getAllRegions(DataProvider g) { - KList r = new KList<>(); - - for (String i : getRegions()) { - r.add(g.getData().getRegionLoader().load(i)); - } - - return r; - } - - public KList getAllAnyRegions() { - KList r = new KList<>(); - - for (String i : getRegions()) { - r.add(IrisData.loadAnyRegion(i)); - } - - return r; - } - - public KList getAllBiomes(DataProvider g) { - return g.getData().getBiomeLoader().loadAll(g.getData().getBiomeLoader().getPossibleKeys()); - } - - public KList getAllAnyBiomes() { - KList r = new KList<>(); - - for (IrisRegion i : getAllAnyRegions()) { - if (i == null) { - continue; - } - - r.addAll(i.getAllAnyBiomes()); - } - - return r; - } - - public IrisGeneratorStyle getBiomeStyle(InferredType type) { - switch (type) { - case CAVE: - return caveBiomeStyle; - case LAND: - return landBiomeStyle; - case SEA: - return seaBiomeStyle; - case SHORE: - return shoreBiomeStyle; - default: - break; - } - - return landBiomeStyle; - } - - public boolean installDataPack(DataProvider data, File datapacks) { - boolean write = false; - boolean changed = false; - - IO.delete(new File(datapacks, "iris/data/" + getLoadKey().toLowerCase())); - - for (IrisBiome i : getAllBiomes(data)) { - if (i.isCustom()) { - write = true; - - for (IrisBiomeCustom j : i.getCustomDerivitives()) { - File output = new File(datapacks, "iris/data/" + getLoadKey().toLowerCase() + "/worldgen/biome/" + j.getId() + ".json"); - - if (!output.exists()) { - changed = true; - } - - Iris.verbose(" Installing Data Pack Biome: " + output.getPath()); - output.getParentFile().mkdirs(); - try { - IO.writeAll(output, j.generateJson()); - } catch (IOException e) { - Iris.reportError(e); - e.printStackTrace(); - } - } - } - } - - if (write) { - File mcm = new File(datapacks, "iris/pack.mcmeta"); - try { - IO.writeAll(mcm, """ - { - "pack": { - "description": "Iris Data Pack. This pack contains all installed Iris Packs' resources.", - "pack_format": 10 - } - } - """); - } catch (IOException e) { - Iris.reportError(e); - e.printStackTrace(); - } - Iris.verbose(" Installing Data Pack MCMeta: " + mcm.getPath()); - } - - return changed; - } - - @Override - public String getFolderName() { - return "dimensions"; - } - - @Override - public String getTypeName() { - return "Dimension"; - } - - @Override - public void scanForErrors(JSONObject p, VolmitSender sender) { - - } -} +/* + * 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.engine.object; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.loader.IrisRegistrant; +import com.volmit.iris.engine.data.cache.AtomicCache; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.object.annotations.*; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.data.DataProvider; +import com.volmit.iris.util.data.Dimension; +import com.volmit.iris.util.io.IO; +import com.volmit.iris.util.json.JSONObject; +import com.volmit.iris.util.math.Position2; +import com.volmit.iris.util.math.RNG; +import com.volmit.iris.util.noise.CNG; +import com.volmit.iris.util.plugin.VolmitSender; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.bukkit.Material; +import org.bukkit.World.Environment; +import org.bukkit.block.data.BlockData; + +import java.io.DataInput; +import java.io.File; +import java.io.IOException; + +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@Desc("Represents a dimension") +@Data +@EqualsAndHashCode(callSuper = false) +public class IrisDimension extends IrisRegistrant { + public static final BlockData STONE = Material.STONE.createBlockData(); + public static final BlockData WATER = Material.WATER.createBlockData(); + private final transient AtomicCache parallaxSize = new AtomicCache<>(); + private final transient AtomicCache rockLayerGenerator = new AtomicCache<>(); + private final transient AtomicCache fluidLayerGenerator = new AtomicCache<>(); + private final transient AtomicCache coordFracture = new AtomicCache<>(); + private final transient AtomicCache sinr = new AtomicCache<>(); + private final transient AtomicCache cosr = new AtomicCache<>(); + private final transient AtomicCache rad = new AtomicCache<>(); + private final transient AtomicCache featuresUsed = new AtomicCache<>(); + private final transient AtomicCache> strongholdsCache = new AtomicCache<>(); + @MinNumber(2) + @Required + @Desc("The human readable name of this dimension") + private String name = "A Dimension"; + @MinNumber(1) + @MaxNumber(2032) + @Desc("Maximum height at which players can be teleported to through gameplay.") + private int logicalHeight = 256; + @RegistryListResource(IrisJigsawStructure.class) + @Desc("If defined, Iris will place the given jigsaw structure where minecraft should place the overworld stronghold.") + private String stronghold; + @Desc("If set to true, Iris will remove chunks to allow visualizing cross sections of chunks easily") + private boolean debugChunkCrossSections = false; + @Desc("Vertically split up the biome palettes with 3 air blocks in between to visualize them") + private boolean explodeBiomePalettes = false; + @Desc("Studio Mode for testing different parts of the world") + private StudioMode studioMode = StudioMode.NORMAL; + @MinNumber(1) + @MaxNumber(16) + @Desc("Customize the palette height explosion") + private int explodeBiomePaletteSize = 3; + @MinNumber(2) + @MaxNumber(16) + @Desc("Every X/Z % debugCrossSectionsMod == 0 cuts the chunk") + private int debugCrossSectionsMod = 3; + @Desc("The average distance between strongholds") + private int strongholdJumpDistance = 1280; + @Desc("Define the maximum strongholds to place") + private int maxStrongholds = 14; + @Desc("Tree growth override settings") + private IrisTreeSettings treeSettings = new IrisTreeSettings(); + @Desc("Spawn Entities in this dimension over time. Iris will continually replenish these mobs just like vanilla does.") + @ArrayType(min = 1, type = String.class) + @RegistryListResource(IrisSpawner.class) + private KList entitySpawners = new KList<>(); + @Desc("Reference loot tables in this area") + private IrisLootReference loot = new IrisLootReference(); + @MinNumber(0) + @Desc("The version of this dimension. Changing this will stop users from accidentally upgrading (and breaking their worlds).") + private int version = 1; + @ArrayType(min = 1, type = IrisBlockDrops.class) + @Desc("Define custom block drops for this dimension") + private KList blockDrops = new KList<>(); + @Desc("Should bedrock be generated or not.") + private boolean bedrock = true; + @MinNumber(0) + @MaxNumber(1) + @Desc("The land chance. Up to 1.0 for total land or 0.0 for total sea") + private double landChance = 0.625; + @Desc("The placement style of regions") + private IrisGeneratorStyle regionStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style(); + @Desc("The placement style of land/sea") + private IrisGeneratorStyle continentalStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style(); + @Desc("The placement style of biomes") + private IrisGeneratorStyle landBiomeStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style(); + @Desc("The placement style of biomes") + private IrisGeneratorStyle shoreBiomeStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style(); + @Desc("The placement style of biomes") + private IrisGeneratorStyle seaBiomeStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style(); + @Desc("The placement style of biomes") + private IrisGeneratorStyle caveBiomeStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style(); + @Desc("Instead of filling objects with air, fills them with cobweb so you can see them") + private boolean debugSmartBore = false; + @Desc("Generate decorations or not") + private boolean decorate = true; + @Desc("Use post processing or not") + private boolean postProcessing = true; + @Desc("Add slabs in post processing") + private boolean postProcessingSlabs = true; + @Desc("Add painted walls in post processing") + private boolean postProcessingWalls = true; + @Desc("Carving configuration for the dimension") + private IrisCarving carving = new IrisCarving(); + @Desc("Configuration of fluid bodies such as rivers & lakes") + private IrisFluidBodies fluidBodies = new IrisFluidBodies(); + @Desc("forceConvertTo320Height") + private Boolean forceConvertTo320Height = false; + @Desc("The world environment") + private Environment environment = Environment.NORMAL; + @RegistryListResource(IrisRegion.class) + @Required + @ArrayType(min = 1, type = String.class) + @Desc("Define all of the regions to include in this dimension. Dimensions -> Regions -> Biomes -> Objects etc") + private KList regions = new KList<>(); + @ArrayType(min = 1, type = IrisJigsawStructurePlacement.class) + @Desc("Jigsaw structures") + private KList jigsawStructures = new KList<>(); + @Required + @MinNumber(0) + @MaxNumber(1024) + @Desc("The fluid height for this dimension") + private int fluidHeight = 63; + @Desc("Define the min and max Y bounds of this dimension. Please keep in mind that Iris internally generates from 0 to (max - min). \n\nFor example at -64 to 320, Iris is internally generating to 0 to 384, then on outputting chunks, it shifts it down by the min height (64 blocks). The default is -64 to 320. \n\nThe fluid height is placed at (fluid height + min height). So a fluid height of 63 would actually show up in the world at 1.") + private IrisRange dimensionHeight = new IrisRange(-64, 320); + @Desc("Enable smart vanilla height") + private boolean smartVanillaHeight = false; + @RegistryListResource(IrisBiome.class) + @Desc("Keep this either undefined or empty. Setting any biome name into this will force iris to only generate the specified biome. Great for testing.") + private String focus = ""; + @RegistryListResource(IrisRegion.class) + @Desc("Keep this either undefined or empty. Setting any region name into this will force iris to only generate the specified region. Great for testing.") + private String focusRegion = ""; + @MinNumber(0.0001) + @MaxNumber(512) + @Desc("Zoom in or out the biome size. Higher = bigger biomes") + private double biomeZoom = 5D; + @MinNumber(0) + @MaxNumber(360) + @Desc("You can rotate the input coordinates by an angle. This can make terrain appear more natural (less sharp corners and lines). This literally rotates the entire dimension by an angle. Hint: Try 12 degrees or something not on a 90 or 45 degree angle.") + private double dimensionAngleDeg = 0; + @Required + @Desc("Define the mode of this dimension (required!)") + private IrisDimensionMode mode = new IrisDimensionMode(); + @MinNumber(0) + @MaxNumber(8192) + @Desc("Coordinate fracturing applies noise to the input coordinates. This creates the 'iris swirls' and wavy features. The distance pushes these waves further into places they shouldnt be. This is a block value multiplier.") + private double coordFractureDistance = 20; + @MinNumber(0.0001) + @MaxNumber(512) + @Desc("Coordinate fracturing zoom. Higher = less frequent warping, Lower = more frequent and rapid warping / swirls.") + private double coordFractureZoom = 8; + @MinNumber(0.0001) + @MaxNumber(512) + @Desc("This zooms in the land space") + private double landZoom = 1; + @MinNumber(0.0001) + @MaxNumber(512) + @Desc("This zooms oceanic biomes") + private double seaZoom = 1; + @MinNumber(0.0001) + @MaxNumber(512) + @Desc("Zoom in continents") + private double continentZoom = 1; + @MinNumber(0.0001) + @MaxNumber(512) + @Desc("Change the size of regions") + private double regionZoom = 1; + @Desc("Disable this to stop placing objects, entities, features & updates") + private boolean useMantle = true; + @Desc("Prevent Leaf decay as if placed in creative mode") + private boolean preventLeafDecay = false; + @ArrayType(min = 1, type = IrisDepositGenerator.class) + @Desc("Define global deposit generators") + private KList deposits = new KList<>(); + @ArrayType(min = 1, type = IrisShapedGeneratorStyle.class) + @Desc("Overlay additional noise on top of the interoplated terrain.") + private KList overlayNoise = new KList<>(); + @Desc("If true, the spawner system has infinite energy. This is NOT recommended because it would allow for mobs to keep spawning over and over without a rate limit") + private boolean infiniteEnergy = false; + @MinNumber(0) + @MaxNumber(10000) + @Desc("This is the maximum energy you can have in a dimension") + private double maximumEnergy = 1000; + @MinNumber(0.0001) + @MaxNumber(512) + @Desc("The rock zoom mostly for zooming in on a wispy palette") + private double rockZoom = 5; + @Desc("The palette of blocks for 'stone'") + private IrisMaterialPalette rockPalette = new IrisMaterialPalette().qclear().qadd("stone"); + @Desc("The palette of blocks for 'water'") + private IrisMaterialPalette fluidPalette = new IrisMaterialPalette().qclear().qadd("water"); + @Desc("Remove cartographers so they do not crash the server (Iris worlds only)") + private boolean removeCartographersDueToCrash = true; + @Desc("Notify players of cancelled cartographer villager in this radius in blocks (set to -1 to disable, -2 for everyone)") + private int notifyPlayersOfCartographerCancelledRadius = 30; + @Desc("Collection of ores to be generated") + @ArrayType(type = IrisOreGenerator.class, min = 1) + private KList ores = new KList<>(); + @MinNumber(0) + @MaxNumber(318) + @Desc("The Subterrain Fluid Layer Height") + private int caveLavaHeight = 8; + + public int getMaxHeight() { + return (int) getDimensionHeight().getMax(); + } + + public int getMinHeight() { + return (int) getDimensionHeight().getMin(); + } + + public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data) { + if (ores.isEmpty()) { + return null; + } + BlockData b = null; + for (IrisOreGenerator i : ores) { + + b = i.generate(x, y, z, rng, data); + if (b != null) { + return b; + } + } + return null; + } + + public KList getStrongholds(long seed) { + return strongholdsCache.aquire(() -> { + KList pos = new KList<>(); + int jump = strongholdJumpDistance; + RNG rng = new RNG((seed * 223) + 12945); + for (int i = 0; i < maxStrongholds + 1; i++) { + int m = i + 1; + pos.add(new Position2( + (int) ((rng.i(jump * i) + (jump * i)) * (rng.b() ? -1D : 1D)), + (int) ((rng.i(jump * i) + (jump * i)) * (rng.b() ? -1D : 1D)) + )); + } + + pos.remove(0); + + return pos; + }); + } + + public int getFluidHeight() { + return fluidHeight - (int) dimensionHeight.getMin(); + } + + public CNG getCoordFracture(RNG rng, int signature) { + return coordFracture.aquire(() -> + { + CNG coordFracture = CNG.signature(rng.nextParallelRNG(signature)); + coordFracture.scale(0.012 / coordFractureZoom); + return coordFracture; + }); + } + + public double getDimensionAngle() { + return rad.aquire(() -> Math.toRadians(dimensionAngleDeg)); + } + + public Environment getEnvironment() { + return environment; + } + + public IrisRange getDimensionHeight() { + return smartVanillaHeight ? new IrisRange(-64, 320) : dimensionHeight; + } + + public boolean hasFocusRegion() { + return !focusRegion.equals(""); + } + + public String getFocusRegion() { + return focusRegion; + } + + public double sinRotate() { + return sinr.aquire(() -> Math.sin(getDimensionAngle())); + } + + public double cosRotate() { + return cosr.aquire(() -> Math.cos(getDimensionAngle())); + } + + public KList getAllRegions(DataProvider g) { + KList r = new KList<>(); + + for (String i : getRegions()) { + r.add(g.getData().getRegionLoader().load(i)); + } + + return r; + } + + public KList getAllAnyRegions() { + KList r = new KList<>(); + + for (String i : getRegions()) { + r.add(IrisData.loadAnyRegion(i)); + } + + return r; + } + + public KList getAllBiomes(DataProvider g) { + return g.getData().getBiomeLoader().loadAll(g.getData().getBiomeLoader().getPossibleKeys()); + } + + public KList getAllAnyBiomes() { + KList r = new KList<>(); + + for (IrisRegion i : getAllAnyRegions()) { + if (i == null) { + continue; + } + + r.addAll(i.getAllAnyBiomes()); + } + + return r; + } + + public IrisGeneratorStyle getBiomeStyle(InferredType type) { + switch (type) { + case CAVE: + return caveBiomeStyle; + case LAND: + return landBiomeStyle; + case SEA: + return seaBiomeStyle; + case SHORE: + return shoreBiomeStyle; + default: + break; + } + + return landBiomeStyle; + } + + public boolean installDataPack(DataProvider data, File datapacks) { + boolean write = false; + boolean changed = false; + + IO.delete(new File(datapacks, "iris/data/" + getLoadKey().toLowerCase())); + + for (IrisBiome i : getAllBiomes(data)) { + if (i.isCustom()) { + write = true; + + for (IrisBiomeCustom j : i.getCustomDerivitives()) { + File output = new File(datapacks, "iris/data/" + getLoadKey().toLowerCase() + "/worldgen/biome/" + j.getId() + ".json"); + + if (!output.exists()) { + changed = true; + } + + Iris.verbose(" Installing Data Pack Biome: " + output.getPath()); + output.getParentFile().mkdirs(); + try { + IO.writeAll(output, j.generateJson()); + } catch (IOException e) { + Iris.reportError(e); + e.printStackTrace(); + } + } + } + } + + if (write) { + File mcm = new File(datapacks, "iris/pack.mcmeta"); + try { + IO.writeAll(mcm, """ + { + "pack": { + "description": "Iris Data Pack. This pack contains all installed Iris Packs' resources.", + "pack_format": 10 + } + } + """); + } catch (IOException e) { + Iris.reportError(e); + e.printStackTrace(); + } + Iris.verbose(" Installing Data Pack MCMeta: " + mcm.getPath()); + } + + return changed; + } + + @Override + public String getFolderName() { + return "dimensions"; + } + + @Override + public String getTypeName() { + return "Dimension"; + } + + @Override + public void scanForErrors(JSONObject p, VolmitSender sender) { + + } +} From 1593bb2088588ce7b2debda357a56b58b2694ea4 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 3 May 2024 19:57:34 +0200 Subject: [PATCH 07/10] make smartVanillaHeight also adjust the logicalHeight --- .../java/com/volmit/iris/engine/object/IrisDimension.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java index 2837ff676..fedf5cf0e 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java @@ -305,6 +305,10 @@ public class IrisDimension extends IrisRegistrant { return smartVanillaHeight ? new IrisRange(-64, 320) : dimensionHeight; } + public int getLogicalHeight() { + return smartVanillaHeight ? 256 : logicalHeight; + } + public boolean hasFocusRegion() { return !focusRegion.equals(""); } From 20b41d65d3fa5935fa07c2fb44db041a1fcffa53 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 3 May 2024 21:01:10 +0200 Subject: [PATCH 08/10] fixes to the per world dimensions + optimizations with the register method --- .../iris/core/nms/v1_19_R1/NMSBinding.java | 53 +++++++-------- .../iris/core/nms/v1_19_R2/NMSBinding.java | 54 +++++++-------- .../iris/core/nms/v1_19_R3/NMSBinding.java | 54 +++++++-------- .../iris/core/nms/v1_20_R1/NMSBinding.java | 54 +++++++-------- .../iris/core/nms/v1_20_R2/NMSBinding.java | 54 +++++++-------- .../iris/core/nms/v1_20_R3/NMSBinding.java | 67 +++++++------------ 6 files changed, 155 insertions(+), 181 deletions(-) diff --git a/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java index 3561eb997..8d01af591 100644 --- a/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java +++ b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java @@ -6,6 +6,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FilenameFilter; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -599,8 +600,25 @@ public class NMSBinding implements INMSBinding { var biomeFiles = biome.listFiles(jsonFilter); if (biomeFiles == null) continue; for (File biomeFile : biomeFiles) { + String json = null; + int tries = 10; + while (json == null && tries-- > 0) { + try { + json = IO.readAll(biomeFile); + } catch (IOException e) { + Iris.error("Failed to read biome " + file.getName() + ":" + biomeFile.getName() + " tries left: " + tries); + if (tries == 0) { + e.printStackTrace(); + } + try { + Thread.sleep(100); + } catch (InterruptedException ignored) {} + } + } + if (json == null) continue; + try { - var value = decode(net.minecraft.world.level.biome.Biome.CODEC, IO.readAll(file)).map(Holder::value).orElse(null); + var value = decode(net.minecraft.world.level.biome.Biome.CODEC, json).map(Holder::value).orElse(null); register(Registry.BIOME_REGISTRY, from(file.getName(), biomeFile), value, replace); } catch (Throwable e) { Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName()); @@ -639,28 +657,15 @@ public class NMSBinding implements INMSBinding { field.setAccessible(true); boolean frozen = field.getBoolean(registry); field.setBoolean(registry, false); - Field holdersField = null; - boolean holders = false; - for (Field f : MappedRegistry.class.getDeclaredFields()) { - if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); - } + Field valueField = getField(Holder.Reference.class, "T"); + valueField.setAccessible(true); try { - registry.createIntrusiveHolder(value); - registry.register(key, value, Lifecycle.stable()); + var holder = registry.register(key, value, Lifecycle.stable()); + if (frozen) valueField.set(holder, value); return true; } finally { field.setBoolean(registry, frozen); - if (holders) { - holdersField.set(registry, null); - } } } catch (Throwable e) { throw new IllegalStateException(e); @@ -777,20 +782,14 @@ public class NMSBinding implements INMSBinding { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { File iris = new File(access.levelDirectory.path().toFile(), "iris"); - if (!iris.exists()) return; + if (!iris.exists() && !key.location().getPath().startsWith("iris/")) return; var logger = MinecraftServer.LOGGER; ResourceKey typeKey = ResourceKey.create(Registry.DIMENSION_TYPE_REGISTRY, new ResourceLocation("iris", key.location().getPath())); RegistryAccess registryAccess = server.registryAccess(); Registry registry = registryAccess.registry(Registry.DIMENSION_TYPE_REGISTRY).orElse(null); - if (registry == null) { - logger.warn("Unable to find registry for dimension type {}", typeKey); - return; - } + if (registry == null) throw new IllegalStateException("Unable to find registry for dimension type " + typeKey); Holder holder = registry.getHolder(typeKey).orElse(null); - if (holder == null) { - logger.warn("Unable to find dimension type {}", typeKey); - return; - } + if (holder == null) throw new IllegalStateException("Unable to find dimension type " + typeKey); levelStem = new LevelStem(holder, levelStem.generator()); } } diff --git a/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java index 03105825c..59c54c2ad 100644 --- a/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java +++ b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java @@ -6,6 +6,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FilenameFilter; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -601,8 +602,25 @@ public class NMSBinding implements INMSBinding { var biomeFiles = biome.listFiles(jsonFilter); if (biomeFiles == null) continue; for (File biomeFile : biomeFiles) { + String json = null; + int tries = 10; + while (json == null && tries-- > 0) { + try { + json = IO.readAll(biomeFile); + } catch (IOException e) { + Iris.error("Failed to read biome " + file.getName() + ":" + biomeFile.getName() + " tries left: " + tries); + if (tries == 0) { + e.printStackTrace(); + } + try { + Thread.sleep(100); + } catch (InterruptedException ignored) {} + } + } + if (json == null) continue; + try { - var value = decode(net.minecraft.world.level.biome.Biome.CODEC, IO.readAll(file)).map(Holder::value).orElse(null); + var value = decode(net.minecraft.world.level.biome.Biome.CODEC, json).map(Holder::value).orElse(null); register(Registries.BIOME, from(file.getName(), biomeFile), value, replace); } catch (Throwable e) { Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName()); @@ -641,28 +659,15 @@ public class NMSBinding implements INMSBinding { field.setAccessible(true); boolean frozen = field.getBoolean(registry); field.setBoolean(registry, false); - Field holdersField = null; - boolean holders = false; - for (Field f : MappedRegistry.class.getDeclaredFields()) { - if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); - } + Field valueField = getField(Holder.Reference.class, "T"); + valueField.setAccessible(true); try { - registry.createIntrusiveHolder(value); - registry.register(key, value, Lifecycle.stable()); + var holder = registry.register(key, value, Lifecycle.stable()); + if (frozen) valueField.set(holder, value); return true; } finally { field.setBoolean(registry, frozen); - if (holders) { - holdersField.set(registry, null); - } } } catch (Throwable e) { throw new IllegalStateException(e); @@ -778,20 +783,13 @@ public class NMSBinding implements INMSBinding { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { File iris = new File(access.levelDirectory.path().toFile(), "iris"); - if (!iris.exists()) return; - var logger = MinecraftServer.LOGGER; + if (!iris.exists() && !key.location().getPath().startsWith("iris/")) return; ResourceKey typeKey = ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", key.location().getPath())); RegistryAccess registryAccess = server.registryAccess(); Registry registry = registryAccess.registry(Registries.DIMENSION_TYPE).orElse(null); - if (registry == null) { - logger.warn("Unable to find registry for dimension type {}", typeKey); - return; - } + if (registry == null) throw new IllegalStateException("Unable to find registry for dimension type " + typeKey); Holder holder = registry.getHolder(typeKey).orElse(null); - if (holder == null) { - logger.warn("Unable to find dimension type {}", typeKey); - return; - } + if (holder == null) throw new IllegalStateException("Unable to find dimension type " + typeKey); levelStem = new LevelStem(holder, levelStem.generator()); } } diff --git a/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java index 8eef4e427..a1396ba1c 100644 --- a/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java +++ b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java @@ -6,6 +6,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FilenameFilter; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -605,8 +606,25 @@ public class NMSBinding implements INMSBinding { var biomeFiles = biome.listFiles(jsonFilter); if (biomeFiles == null) continue; for (File biomeFile : biomeFiles) { + String json = null; + int tries = 10; + while (json == null && tries-- > 0) { + try { + json = IO.readAll(biomeFile); + } catch (IOException e) { + Iris.error("Failed to read biome " + file.getName() + ":" + biomeFile.getName() + " tries left: " + tries); + if (tries == 0) { + e.printStackTrace(); + } + try { + Thread.sleep(100); + } catch (InterruptedException ignored) {} + } + } + if (json == null) continue; + try { - var value = decode(net.minecraft.world.level.biome.Biome.CODEC, IO.readAll(file)).map(Holder::value).orElse(null); + var value = decode(net.minecraft.world.level.biome.Biome.CODEC, json).map(Holder::value).orElse(null); register(Registries.BIOME, from(file.getName(), biomeFile), value, replace); } catch (Throwable e) { Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName()); @@ -645,28 +663,15 @@ public class NMSBinding implements INMSBinding { field.setAccessible(true); boolean frozen = field.getBoolean(registry); field.setBoolean(registry, false); - Field holdersField = null; - boolean holders = false; - for (Field f : MappedRegistry.class.getDeclaredFields()) { - if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); - } + Field valueField = getField(Holder.Reference.class, "T"); + valueField.setAccessible(true); try { - registry.createIntrusiveHolder(value); - registry.register(key, value, Lifecycle.stable()); + var holder = registry.register(key, value, Lifecycle.stable()); + if (frozen) valueField.set(holder, value); return true; } finally { field.setBoolean(registry, frozen); - if (holders) { - holdersField.set(registry, null); - } } } catch (Throwable e) { throw new IllegalStateException(e); @@ -782,20 +787,13 @@ public class NMSBinding implements INMSBinding { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { File iris = new File(access.levelDirectory.path().toFile(), "iris"); - if (!iris.exists()) return; - var logger = MinecraftServer.LOGGER; + if (!iris.exists() && !key.location().getPath().startsWith("iris/")) return; ResourceKey typeKey = ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", key.location().getPath())); RegistryAccess registryAccess = server.registryAccess(); Registry registry = registryAccess.registry(Registries.DIMENSION_TYPE).orElse(null); - if (registry == null) { - logger.warn("Unable to find registry for dimension type {}", typeKey); - return; - } + if (registry == null) throw new IllegalStateException("Unable to find registry for dimension type " + typeKey); Holder holder = registry.getHolder(typeKey).orElse(null); - if (holder == null) { - logger.warn("Unable to find dimension type {}", typeKey); - return; - } + if (holder == null) throw new IllegalStateException("Unable to find dimension type " + typeKey); levelStem = new LevelStem(holder, levelStem.generator()); } } 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 7e6e8c963..ab01736a8 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 @@ -88,6 +88,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FilenameFilter; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -592,8 +593,25 @@ public class NMSBinding implements INMSBinding { var biomeFiles = biome.listFiles(jsonFilter); if (biomeFiles == null) continue; for (File biomeFile : biomeFiles) { + String json = null; + int tries = 10; + while (json == null && tries-- > 0) { + try { + json = IO.readAll(biomeFile); + } catch (IOException e) { + Iris.error("Failed to read biome " + file.getName() + ":" + biomeFile.getName() + " tries left: " + tries); + if (tries == 0) { + e.printStackTrace(); + } + try { + Thread.sleep(100); + } catch (InterruptedException ignored) {} + } + } + if (json == null) continue; + try { - var value = decode(net.minecraft.world.level.biome.Biome.CODEC, IO.readAll(file)).map(Holder::value).orElse(null); + var value = decode(net.minecraft.world.level.biome.Biome.CODEC, json).map(Holder::value).orElse(null); register(Registries.BIOME, from(file.getName(), biomeFile), value, replace); } catch (Throwable e) { Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName()); @@ -632,28 +650,15 @@ public class NMSBinding implements INMSBinding { field.setAccessible(true); boolean frozen = field.getBoolean(registry); field.setBoolean(registry, false); - Field holdersField = null; - boolean holders = false; - for (Field f : MappedRegistry.class.getDeclaredFields()) { - if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); - } + Field valueField = getField(Holder.Reference.class, "T"); + valueField.setAccessible(true); try { - registry.createIntrusiveHolder(value); - registry.register(key, value, Lifecycle.stable()); + var holder = registry.register(key, value, Lifecycle.stable()); + if (frozen) valueField.set(holder, value); return true; } finally { field.setBoolean(registry, frozen); - if (holders) { - holdersField.set(registry, null); - } } } catch (Throwable e) { throw new IllegalStateException(e); @@ -782,20 +787,13 @@ public class NMSBinding implements INMSBinding { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { File iris = new File(access.levelDirectory.path().toFile(), "iris"); - if (!iris.exists()) return; - var logger = MinecraftServer.LOGGER; + if (!iris.exists() && !key.location().getPath().startsWith("iris/")) return; ResourceKey typeKey = ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", key.location().getPath())); RegistryAccess registryAccess = server.registryAccess(); Registry registry = registryAccess.registry(Registries.DIMENSION_TYPE).orElse(null); - if (registry == null) { - logger.warn("Unable to find registry for dimension type {}", typeKey); - return; - } + if (registry == null) throw new IllegalStateException("Unable to find registry for dimension type " + typeKey); Holder holder = registry.getHolder(typeKey).orElse(null); - if (holder == null) { - logger.warn("Unable to find dimension type {}", typeKey); - return; - } + if (holder == null) throw new IllegalStateException("Unable to find dimension type " + typeKey); levelStem = new LevelStem(holder, levelStem.generator()); } } diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java index 724b52dc1..e4af087ec 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 @@ -6,6 +6,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FilenameFilter; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -602,8 +603,25 @@ public class NMSBinding implements INMSBinding { var biomeFiles = biome.listFiles(jsonFilter); if (biomeFiles == null) continue; for (File biomeFile : biomeFiles) { + String json = null; + int tries = 10; + while (json == null && tries-- > 0) { + try { + json = IO.readAll(biomeFile); + } catch (IOException e) { + Iris.error("Failed to read biome " + file.getName() + ":" + biomeFile.getName() + " tries left: " + tries); + if (tries == 0) { + e.printStackTrace(); + } + try { + Thread.sleep(100); + } catch (InterruptedException ignored) {} + } + } + if (json == null) continue; + try { - var value = decode(net.minecraft.world.level.biome.Biome.CODEC, IO.readAll(file)).map(Holder::value).orElse(null); + var value = decode(net.minecraft.world.level.biome.Biome.CODEC, json).map(Holder::value).orElse(null); register(Registries.BIOME, from(file.getName(), biomeFile), value, replace); } catch (Throwable e) { Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName()); @@ -642,28 +660,15 @@ public class NMSBinding implements INMSBinding { field.setAccessible(true); boolean frozen = field.getBoolean(registry); field.setBoolean(registry, false); - Field holdersField = null; - boolean holders = false; - for (Field f : MappedRegistry.class.getDeclaredFields()) { - if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); - } + Field valueField = getField(Holder.Reference.class, "T"); + valueField.setAccessible(true); try { - registry.createIntrusiveHolder(value); - registry.register(key, value, Lifecycle.stable()); + var holder = registry.register(key, value, Lifecycle.stable()); + if (frozen) valueField.set(holder, value); return true; } finally { field.setBoolean(registry, frozen); - if (holders) { - holdersField.set(registry, null); - } } } catch (Throwable e) { throw new IllegalStateException(e); @@ -784,20 +789,13 @@ public class NMSBinding implements INMSBinding { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { File iris = new File(access.levelDirectory.path().toFile(), "iris"); - if (!iris.exists()) return; - var logger = MinecraftServer.LOGGER; + if (!iris.exists() && !key.location().getPath().startsWith("iris/")) return; ResourceKey typeKey = ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", key.location().getPath())); RegistryAccess registryAccess = server.registryAccess(); Registry registry = registryAccess.registry(Registries.DIMENSION_TYPE).orElse(null); - if (registry == null) { - logger.warn("Unable to find registry for dimension type {}", typeKey); - return; - } + if (registry == null) throw new IllegalStateException("Unable to find registry for dimension type " + typeKey); Holder holder = registry.getHolder(typeKey).orElse(null); - if (holder == null) { - logger.warn("Unable to find dimension type {}", typeKey); - return; - } + if (holder == null) throw new IllegalStateException("Unable to find dimension type " + typeKey); levelStem = new LevelStem(holder, levelStem.generator()); } } diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java index cb67298e0..5f56f1299 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 @@ -7,21 +7,15 @@ import java.io.DataOutputStream; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.Instrumentation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.nio.file.Files; import java.util.*; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Logger; import com.google.common.base.Preconditions; -import com.google.common.base.Supplier; import com.google.gson.JsonElement; -import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; @@ -29,27 +23,19 @@ import com.mojang.serialization.JsonOps; import com.mojang.serialization.Lifecycle; import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.format.C; -import com.volmit.iris.util.function.NastySupplier; import com.volmit.iris.util.io.IO; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import net.bytebuddy.ByteBuddy; -import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.loading.ClassReloadingStrategy; import net.bytebuddy.matcher.ElementMatchers; -import net.bytebuddy.utility.JavaModule; import net.minecraft.core.IdMapper; import net.minecraft.core.MappedRegistry; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.util.GsonHelper; -import net.minecraft.util.worldupdate.WorldUpgrader; import net.minecraft.world.RandomSequences; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.storage.LevelStorageSource; @@ -615,8 +601,25 @@ public class NMSBinding implements INMSBinding { var biomeFiles = biome.listFiles(jsonFilter); if (biomeFiles == null) continue; for (File biomeFile : biomeFiles) { + String json = null; + int tries = 10; + while (json == null && tries-- > 0) { + try { + json = IO.readAll(biomeFile); + } catch (IOException e) { + Iris.error("Failed to read biome " + file.getName() + ":" + biomeFile.getName() + " tries left: " + tries); + if (tries == 0) { + e.printStackTrace(); + } + try { + Thread.sleep(100); + } catch (InterruptedException ignored) {} + } + } + if (json == null) continue; + try { - var value = decode(net.minecraft.world.level.biome.Biome.CODEC, IO.readAll(file)).map(Holder::value).orElse(null); + var value = decode(net.minecraft.world.level.biome.Biome.CODEC, json).map(Holder::value).orElse(null); register(Registries.BIOME, from(file.getName(), biomeFile), value, replace); } catch (Throwable e) { Iris.error("Failed to register biome " + file.getName() + ":" + biomeFile.getName()); @@ -655,28 +658,15 @@ public class NMSBinding implements INMSBinding { field.setAccessible(true); boolean frozen = field.getBoolean(registry); field.setBoolean(registry, false); - Field holdersField = null; - boolean holders = false; - for (Field f : MappedRegistry.class.getDeclaredFields()) { - if (!f.getGenericType().getTypeName().startsWith("java.util.Map()); - } + Field valueField = getField(Holder.Reference.class, "T"); + valueField.setAccessible(true); try { - registry.createIntrusiveHolder(value); - registry.register(key, value, Lifecycle.stable()); + var holder = registry.register(key, value, Lifecycle.stable()); + if (frozen) valueField.set(holder, value); return true; } finally { field.setBoolean(registry, frozen); - if (holders) { - holdersField.set(registry, null); - } } } catch (Throwable e) { throw new IllegalStateException(e); @@ -796,20 +786,13 @@ public class NMSBinding implements INMSBinding { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { File iris = new File(access.levelDirectory.path().toFile(), "iris"); - if (!iris.exists()) return; - var logger = MinecraftServer.LOGGER; + if (!iris.exists() && !key.location().getPath().startsWith("iris/")) return; ResourceKey typeKey = ResourceKey.create(Registries.DIMENSION_TYPE, new ResourceLocation("iris", key.location().getPath())); RegistryAccess registryAccess = server.registryAccess(); Registry registry = registryAccess.registry(Registries.DIMENSION_TYPE).orElse(null); - if (registry == null) { - logger.warn("Unable to find registry for dimension type {}", typeKey); - return; - } + if (registry == null) throw new IllegalStateException("Unable to find registry for dimension type " + typeKey); Holder holder = registry.getHolder(typeKey).orElse(null); - if (holder == null) { - logger.warn("Unable to find dimension type {}", typeKey); - return; - } + if (holder == null) throw new IllegalStateException("Unable to find dimension type " + typeKey); levelStem = new LevelStem(holder, levelStem.generator()); } } From 9e6035e7b43d9c65804f32ba5553ed36d421043a Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 3 May 2024 21:17:02 +0200 Subject: [PATCH 09/10] remove debug code --- core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java | 4 +--- 1 file changed, 1 insertion(+), 3 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 0ed630cf4..48637dc16 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 @@ -116,9 +116,7 @@ public interface INMSBinding { boolean loadDatapack(File datapackFolder, boolean replace); - default boolean registerDimension(String name, IrisDimension dimension) { - return false; - } + boolean registerDimension(String name, IrisDimension dimension); void injectBukkit(); } From 12abc1709e2188bccef9677a04ab9ce84668d9c8 Mon Sep 17 00:00:00 2001 From: CrazyDev22 Date: Fri, 3 May 2024 21:18:00 +0200 Subject: [PATCH 10/10] add missing method to NMSBinding1X --- .../java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 c928db7aa..42a5e2507 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 @@ -23,6 +23,7 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.BlockPos; import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.format.C; @@ -111,6 +112,11 @@ public class NMSBinding1X implements INMSBinding { return false; } + @Override + public boolean registerDimension(String name, IrisDimension dimension) { + return false; + } + @Override public void deserializeTile(CompoundTag s, Location newPosition) {