From d6bd5ec82f174db72c5ec924615575261821e7ae Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Wed, 18 Feb 2026 20:24:25 -0500 Subject: [PATCH] d --- core/src/main/java/art/arcane/iris/Iris.java | 11 +- .../arcane/iris/core/ServerConfigurator.java | 15 +- .../iris/core/commands/CommandDeveloper.java | 2 +- .../iris/core/commands/CommandObject.java | 4 +- .../iris/core/commands/CommandWhat.java | 44 +++- .../iris/core/edit/BukkitBlockEditor.java | 10 +- .../iris/core/link/FoliaWorldsLink.java | 44 +++- .../art/arcane/iris/core/loader/IrisData.java | 9 +- .../core/loader/ObjectResourceLoader.java | 7 +- .../iris/core/nms/BiomeBaseInjector.java | 28 -- .../art/arcane/iris/core/nms/INMSBinding.java | 8 +- .../nms/datapack/v1206/DataFixerV1206.java | 21 +- .../nms/datapack/v1217/DataFixerV1217.java | 4 +- .../iris/core/nms/v1X/NMSBinding1X.java | 20 +- .../core/pregenerator/LazyPregenerator.java | 15 +- .../iris/core/project/SchemaBuilder.java | 19 +- .../arcane/iris/core/service/BoardSVC.java | 3 +- .../art/arcane/iris/core/service/EditSVC.java | 15 ++ .../iris/core/service/IrisEngineSVC.java | 8 +- .../iris/core/service/PreservationSVC.java | 4 +- .../arcane/iris/core/tools/IrisCreator.java | 245 +++++++++++++++++- .../engine/data/chunk/LinkedTerrainChunk.java | 159 +++--------- .../engine/data/chunk/MCATerrainChunk.java | 167 ------------ .../iris/engine/data/chunk/TerrainChunk.java | 101 +------- .../engine/decorator/IrisEngineDecorator.java | 6 +- .../arcane/iris/engine/framework/Engine.java | 12 +- .../framework/placer/WorldObjectPlacer.java | 3 +- .../iris/engine/jigsaw/PlannedPiece.java | 19 +- .../iris/engine/jigsaw/PlannedStructure.java | 12 +- .../engine/object/IrisAttributeModifier.java | 27 +- .../iris/engine/object/IrisBiomeCustom.java | 20 +- .../arcane/iris/engine/object/IrisEffect.java | 10 +- .../iris/engine/object/IrisEnchantment.java | 3 +- .../arcane/iris/engine/object/IrisEntity.java | 6 +- .../arcane/iris/engine/object/IrisLoot.java | 35 +-- .../iris/engine/object/IrisPotionEffect.java | 10 +- .../iris/engine/object/LegacyTileData.java | 120 +++++++-- .../arcane/iris/engine/object/TileData.java | 17 +- .../functions/LootTableKeyFunction.java | 5 +- .../engine/platform/BukkitChunkGenerator.java | 23 +- .../iris/engine/platform/DummyBiomeGrid.java | 29 --- .../art/arcane/iris/util/common/data/B.java | 7 +- .../art/arcane/iris/util/common/format/C.java | 4 +- .../iris/util/common/nbt/mca/NBTWorld.java | 13 +- .../iris/util/common/reflect/KeyedType.java | 49 +++- .../common/scheduling/jobs/DownloadJob.java | 4 +- .../arcane/iris/util/project/hunk/Hunk.java | 14 +- .../hunk/view/BiomeGridHunkHolder.java | 45 ---- .../project/hunk/view/BiomeGridHunkView.java | 45 ---- .../hunk/view/TerrainChunkBiomeHunkView.java | 43 +++ .../art/arcane/iris/core/safeguard/Mode.kt | 2 +- .../core/nms/v1_21_R7/CustomBiomeSource.java | 145 +++++++++-- .../core/nms/v1_21_R7/IrisChunkGenerator.java | 6 - .../iris/core/nms/v1_21_R7/NMSBinding.java | 134 ++++++---- settings.gradle.kts | 32 ++- 55 files changed, 1063 insertions(+), 800 deletions(-) delete mode 100644 core/src/main/java/art/arcane/iris/core/nms/BiomeBaseInjector.java delete mode 100644 core/src/main/java/art/arcane/iris/engine/data/chunk/MCATerrainChunk.java delete mode 100644 core/src/main/java/art/arcane/iris/engine/platform/DummyBiomeGrid.java delete mode 100644 core/src/main/java/art/arcane/iris/util/project/hunk/view/BiomeGridHunkHolder.java delete mode 100644 core/src/main/java/art/arcane/iris/util/project/hunk/view/BiomeGridHunkView.java create mode 100644 core/src/main/java/art/arcane/iris/util/project/hunk/view/TerrainChunkBiomeHunkView.java diff --git a/core/src/main/java/art/arcane/iris/Iris.java b/core/src/main/java/art/arcane/iris/Iris.java index 9e093a5b2..84ce532b0 100644 --- a/core/src/main/java/art/arcane/iris/Iris.java +++ b/core/src/main/java/art/arcane/iris/Iris.java @@ -76,7 +76,7 @@ import org.jetbrains.annotations.Nullable; import java.io.*; import java.lang.annotation.Annotation; -import java.net.URL; +import java.net.URI; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; @@ -232,7 +232,7 @@ public class Iris extends VolmitPlugin implements Listener { File f = Iris.instance.getDataFile("cache", h.substring(0, 2), h.substring(3, 5), h); if (!f.exists()) { - try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) { + try (BufferedInputStream in = new BufferedInputStream(URI.create(url).toURL().openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) { byte[] dataBuffer = new byte[1024]; int bytesRead; while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { @@ -251,7 +251,7 @@ public class Iris extends VolmitPlugin implements Listener { String h = IO.hash(name + "*" + url); File f = Iris.instance.getDataFile("cache", h.substring(0, 2), h.substring(3, 5), h); - try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) { + try (BufferedInputStream in = new BufferedInputStream(URI.create(url).toURL().openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) { byte[] dataBuffer = new byte[1024]; int bytesRead; while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { @@ -274,7 +274,7 @@ public class Iris extends VolmitPlugin implements Listener { String h = IO.hash(name + "*" + url); File f = Iris.instance.getDataFile("cache", h.substring(0, 2), h.substring(3, 5), h); Iris.verbose("Download " + name + " -> " + url); - try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) { + try (BufferedInputStream in = new BufferedInputStream(URI.create(url).toURL().openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) { byte[] dataBuffer = new byte[1024]; int bytesRead; while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { @@ -360,7 +360,6 @@ public class Iris extends VolmitPlugin implements Listener { } } - @SuppressWarnings("deprecation") public static void later(NastyRunnable object) { try { J.a(() -> { @@ -459,7 +458,7 @@ public class Iris extends VolmitPlugin implements Listener { PrintWriter pw = new PrintWriter(fos); for (Thread i : f.keySet()) { pw.println("========================================"); - pw.println("Thread: '" + i.getName() + "' ID: " + i.getId() + " STATUS: " + i.getState().name()); + pw.println("Thread: '" + i.getName() + "' ID: " + i.threadId() + " STATUS: " + i.getState().name()); for (StackTraceElement j : f.get(i)) { pw.println(" @ " + j.toString()); diff --git a/core/src/main/java/art/arcane/iris/core/ServerConfigurator.java b/core/src/main/java/art/arcane/iris/core/ServerConfigurator.java index 87459579f..2faaa08ae 100644 --- a/core/src/main/java/art/arcane/iris/core/ServerConfigurator.java +++ b/core/src/main/java/art/arcane/iris/core/ServerConfigurator.java @@ -32,7 +32,6 @@ import art.arcane.iris.util.common.misc.ServerProperties; import art.arcane.iris.util.common.plugin.VolmitSender; import art.arcane.iris.util.common.scheduling.J; import lombok.NonNull; -import lombok.SneakyThrows; import org.bukkit.Bukkit; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; @@ -42,8 +41,7 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; +import java.util.Arrays; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicIntegerArray; @@ -243,8 +241,9 @@ public class ServerConfigurator { } public static Stream allPacks() { - return Stream.concat(listFiles(Iris.instance.getDataFolder("packs")) - .filter(File::isDirectory) + File[] packs = Iris.instance.getDataFolder("packs").listFiles(File::isDirectory); + Stream locals = packs == null ? Stream.empty() : Arrays.stream(packs); + return Stream.concat(locals .filter( base -> { var content = new File(base, "dimensions").listFiles(); return content != null && content.length > 0; @@ -263,12 +262,6 @@ public class ServerConfigurator { return path.substring(worldContainer.length(), path.length() - l); } - @SneakyThrows - private static Stream listFiles(File parent) { - if (!parent.isDirectory()) return Stream.empty(); - return Files.walk(parent.toPath()).map(Path::toFile); - } - public static class DimensionHeight { private final IDataFixer fixer; private final AtomicIntegerArray[] dimensions = new AtomicIntegerArray[3]; diff --git a/core/src/main/java/art/arcane/iris/core/commands/CommandDeveloper.java b/core/src/main/java/art/arcane/iris/core/commands/CommandDeveloper.java index 0c3552acf..832706797 100644 --- a/core/src/main/java/art/arcane/iris/core/commands/CommandDeveloper.java +++ b/core/src/main/java/art/arcane/iris/core/commands/CommandDeveloper.java @@ -350,7 +350,7 @@ public class CommandDeveloper implements DirectorExecutor { for (Thread i : f.keySet()) { pw.println("========================================"); - pw.println("Thread: '" + i.getName() + "' ID: " + i.getId() + " STATUS: " + i.getState().name()); + pw.println("Thread: '" + i.getName() + "' ID: " + i.threadId() + " STATUS: " + i.getState().name()); for (StackTraceElement j : f.get(i)) { pw.println(" @ " + j.toString()); diff --git a/core/src/main/java/art/arcane/iris/core/commands/CommandObject.java b/core/src/main/java/art/arcane/iris/core/commands/CommandObject.java index 7454233e7..73129e923 100644 --- a/core/src/main/java/art/arcane/iris/core/commands/CommandObject.java +++ b/core/src/main/java/art/arcane/iris/core/commands/CommandObject.java @@ -289,7 +289,7 @@ public class CommandObject implements DirectorExecutor { } else { g[1] = player().getLocation().getBlock().getLocation().clone().add(0, -1, 0); } - player().setItemInHand(WandSVC.createWand(g[0], g[1])); + player().getInventory().setItemInMainHand(WandSVC.createWand(g[0], g[1])); } } @@ -316,7 +316,7 @@ public class CommandObject implements DirectorExecutor { } else { g[0] = player().getLocation().getBlock().getLocation().clone().add(0, -1, 0); } - player().setItemInHand(WandSVC.createWand(g[0], g[1])); + player().getInventory().setItemInMainHand(WandSVC.createWand(g[0], g[1])); } } diff --git a/core/src/main/java/art/arcane/iris/core/commands/CommandWhat.java b/core/src/main/java/art/arcane/iris/core/commands/CommandWhat.java index bb83936d6..18692d148 100644 --- a/core/src/main/java/art/arcane/iris/core/commands/CommandWhat.java +++ b/core/src/main/java/art/arcane/iris/core/commands/CommandWhat.java @@ -36,9 +36,11 @@ import art.arcane.iris.util.common.scheduling.J; import org.bukkit.Chunk; import org.bukkit.FluidCollisionMode; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; +import java.lang.reflect.Method; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; @@ -69,13 +71,17 @@ public class CommandWhat implements DirectorExecutor { public void biome() { try { IrisBiome b = engine().getBiome(player().getLocation().getBlockX(), player().getLocation().getBlockY() - player().getWorld().getMinHeight(), player().getLocation().getBlockZ()); - sender().sendMessage("IBiome: " + b.getLoadKey() + " (" + b.getDerivative().name() + ")"); + Biome derivative = b.getDerivative(); + NamespacedKey derivativeKey = resolveBiomeKey(derivative); + sender().sendMessage("IBiome: " + b.getLoadKey() + " (" + (derivativeKey == null ? "unregistered" : derivativeKey.getKey()) + ")"); } catch (Throwable e) { Iris.reportError(e); - sender().sendMessage("Non-Iris Biome: " + player().getLocation().getBlock().getBiome().name()); + Biome biome = player().getLocation().getBlock().getBiome(); + NamespacedKey key = resolveBiomeKey(biome); + sender().sendMessage("Non-Iris Biome: " + (key == null ? "unregistered" : key)); - if (player().getLocation().getBlock().getBiome().equals(Biome.CUSTOM)) { + if (key == null || key.getKey().equals("custom")) { try { sender().sendMessage("Data Pack Biome: " + INMS.get().getTrueBiomeBaseKey(player().getLocation()) + " (ID: " + INMS.get().getTrueBiomeBaseId(INMS.get().getTrueBiomeBase(player().getLocation())) + ")"); } catch (Throwable ee) { @@ -166,4 +172,36 @@ public class CommandWhat implements DirectorExecutor { sender().sendMessage(C.IRIS + "Iris worlds only."); } } + + private NamespacedKey resolveBiomeKey(Biome biome) { + Object keyOrNullValue = invokeNoThrow(biome, "getKeyOrNull"); + if (keyOrNullValue instanceof NamespacedKey namespacedKey) { + return namespacedKey; + } + + Object keyOrThrowValue = invokeNoThrow(biome, "getKeyOrThrow"); + if (keyOrThrowValue instanceof NamespacedKey namespacedKey) { + return namespacedKey; + } + + Object keyValue = invokeNoThrow(biome, "getKey"); + if (keyValue instanceof NamespacedKey namespacedKey) { + return namespacedKey; + } + + return null; + } + + private Object invokeNoThrow(Biome biome, String methodName) { + if (biome == null) { + return null; + } + + try { + Method method = biome.getClass().getMethod(methodName); + return method.invoke(biome); + } catch (Throwable ignored) { + return null; + } + } } diff --git a/core/src/main/java/art/arcane/iris/core/edit/BukkitBlockEditor.java b/core/src/main/java/art/arcane/iris/core/edit/BukkitBlockEditor.java index a2a18d51b..08fea134c 100644 --- a/core/src/main/java/art/arcane/iris/core/edit/BukkitBlockEditor.java +++ b/core/src/main/java/art/arcane/iris/core/edit/BukkitBlockEditor.java @@ -51,10 +51,13 @@ public class BukkitBlockEditor implements BlockEditor { return M.ms(); } - @SuppressWarnings("deprecation") @Override public void setBiome(int x, int z, Biome b) { - world.setBiome(x, z, b); + int minHeight = world.getMinHeight(); + int maxHeight = world.getMaxHeight(); + for (int y = minHeight; y < maxHeight; y++) { + world.setBiome(x, y, z, b); + } } @Override @@ -67,9 +70,8 @@ public class BukkitBlockEditor implements BlockEditor { return world.getBiome(x, y, z); } - @SuppressWarnings("deprecation") @Override public Biome getBiome(int x, int z) { - return world.getBiome(x, z); + return world.getBiome(x, world.getMinHeight(), z); } } diff --git a/core/src/main/java/art/arcane/iris/core/link/FoliaWorldsLink.java b/core/src/main/java/art/arcane/iris/core/link/FoliaWorldsLink.java index f3d2e4723..aaaa839b5 100644 --- a/core/src/main/java/art/arcane/iris/core/link/FoliaWorldsLink.java +++ b/core/src/main/java/art/arcane/iris/core/link/FoliaWorldsLink.java @@ -1,6 +1,8 @@ package art.arcane.iris.core.link; import art.arcane.iris.Iris; +import art.arcane.iris.core.nms.INMS; +import art.arcane.iris.engine.platform.PlatformChunkGenerator; import art.arcane.iris.util.common.scheduling.J; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -274,8 +276,8 @@ public class FoliaWorldsLink { Object primaryLevelData = createPrimaryLevelData(levelStorageAccess, creator.name()); Object runtimeStemKey = createRuntimeLevelStemKey(creator.name()); Object worldLoadingInfo = createWorldLoadingInfo(creator.name(), runtimeStemKey); - Object overworldLevelStem = getOverworldLevelStem(); - Object[] createLevelArgs = new Object[]{overworldLevelStem, worldLoadingInfo, levelStorageAccess, primaryLevelData}; + Object levelStem = resolveCreateLevelStem(creator); + Object[] createLevelArgs = new Object[]{levelStem, worldLoadingInfo, levelStorageAccess, primaryLevelData}; Method createLevelMethod = minecraftServerCreateLevelMethod; if (createLevelMethod == null || !matches(createLevelMethod.getParameterTypes(), createLevelArgs)) { createLevelMethod = resolveMethod(minecraftServer.getClass(), "createLevel", createLevelArgs); @@ -376,6 +378,44 @@ public class FoliaWorldsLink { return createMethod.invoke(null, levelStemRegistryKey, identifier); } + private Object resolveCreateLevelStem(WorldCreator creator) throws ReflectiveOperationException { + Object irisLevelStem = resolveIrisLevelStem(creator); + if (irisLevelStem != null) { + return irisLevelStem; + } + + return getOverworldLevelStem(); + } + + private Object resolveIrisLevelStem(WorldCreator creator) throws ReflectiveOperationException { + ChunkGenerator generator = creator.generator(); + if (!(generator instanceof PlatformChunkGenerator)) { + return null; + } + + Object registryAccess = invoke(minecraftServer, "registryAccess"); + Object binding = INMS.get(); + Method levelStemMethod; + try { + levelStemMethod = resolveMethod(binding.getClass(), "levelStem", registryAccess, generator); + } catch (NoSuchMethodException e) { + throw new IllegalStateException("Iris NMS binding does not expose levelStem(RegistryAccess, ChunkGenerator) for runtime world \"" + creator.name() + "\".", e); + } + + Object levelStem; + try { + levelStem = levelStemMethod.invoke(binding, registryAccess, generator); + } catch (InvocationTargetException e) { + Throwable cause = unwrap(e); + throw new IllegalStateException("Iris failed to resolve runtime level stem for world \"" + creator.name() + "\".", cause); + } + + if (levelStem == null) { + throw new IllegalStateException("Iris resolved a null runtime level stem for world \"" + creator.name() + "\"."); + } + return levelStem; + } + private Object getOverworldLevelStem() throws ReflectiveOperationException { Object levelStemRegistryKey = Class.forName("net.minecraft.core.registries.Registries") .getField("LEVEL_STEM") diff --git a/core/src/main/java/art/arcane/iris/core/loader/IrisData.java b/core/src/main/java/art/arcane/iris/core/loader/IrisData.java index 31afb74fa..7f829b94c 100644 --- a/core/src/main/java/art/arcane/iris/core/loader/IrisData.java +++ b/core/src/main/java/art/arcane/iris/core/loader/IrisData.java @@ -352,7 +352,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { builder = new GsonBuilder() .addDeserializationExclusionStrategy(this) .addSerializationExclusionStrategy(this) - .setLenient() + .setStrictness(Strictness.LENIENT) .registerTypeAdapterFactory(this) .registerTypeAdapter(MantleFlag.class, new MantleFlagAdapter()) .setPrettyPrinting(); @@ -409,19 +409,18 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { public Set> resolveSnippets() { var result = new HashSet>(); var processed = new HashSet>(); - var excluder = gson.excluder(); var queue = new LinkedList>(loaders.keySet()); while (!queue.isEmpty()) { var type = queue.poll(); - if (excluder.excludeClass(type, false) || !processed.add(type)) + if (shouldSkipClass(type) || !processed.add(type)) continue; if (type.isAnnotationPresent(Snippet.class)) result.add(type); try { for (var field : type.getDeclaredFields()) { - if (excluder.excludeField(field, false)) + if (shouldSkipField(new FieldAttributes(field))) continue; queue.add(field.getType()); @@ -590,4 +589,4 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { b.complete(); Iris.info("Loaded Prefetch Cache to reduce generation disk use."); } -} \ No newline at end of file +} diff --git a/core/src/main/java/art/arcane/iris/core/loader/ObjectResourceLoader.java b/core/src/main/java/art/arcane/iris/core/loader/ObjectResourceLoader.java index 44e540772..a986f9c94 100644 --- a/core/src/main/java/art/arcane/iris/core/loader/ObjectResourceLoader.java +++ b/core/src/main/java/art/arcane/iris/core/loader/ObjectResourceLoader.java @@ -59,7 +59,12 @@ public class ObjectResourceLoader extends ResourceLoader { return t; } catch (Throwable e) { Iris.reportError(e); - Iris.warn("Couldn't read " + resourceTypeName + " file: " + j.getPath() + ": " + e.getMessage()); + String message = e.getMessage(); + String reason = e.getClass().getSimpleName(); + if (message != null && !message.isBlank()) { + reason = reason + ": " + message; + } + Iris.warn("Couldn't read " + resourceTypeName + " file: " + j.getPath() + " (" + reason + ")"); return null; } } diff --git a/core/src/main/java/art/arcane/iris/core/nms/BiomeBaseInjector.java b/core/src/main/java/art/arcane/iris/core/nms/BiomeBaseInjector.java deleted file mode 100644 index eb19f489e..000000000 --- a/core/src/main/java/art/arcane/iris/core/nms/BiomeBaseInjector.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2022 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.core.nms; - -@FunctionalInterface -public interface BiomeBaseInjector { - default void setBiome(int x, int z, Object biomeBase) { - setBiome(x, 0, z, biomeBase); - } - - void setBiome(int x, int y, int z, Object biomeBase); -} diff --git a/core/src/main/java/art/arcane/iris/core/nms/INMSBinding.java b/core/src/main/java/art/arcane/iris/core/nms/INMSBinding.java index 21602fb8e..1c12ee6f0 100644 --- a/core/src/main/java/art/arcane/iris/core/nms/INMSBinding.java +++ b/core/src/main/java/art/arcane/iris/core/nms/INMSBinding.java @@ -39,7 +39,6 @@ import org.bukkit.block.Biome; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.event.entity.CreatureSpawnEvent; -import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import java.awt.Color; @@ -104,6 +103,11 @@ public interface INMSBinding { default CompletableFuture createWorldAsync(WorldCreator c) { try { + if (c.generator() instanceof PlatformChunkGenerator gen + && missingDimensionTypes(gen.getTarget().getDimension().getDimensionTypeKey())) { + return CompletableFuture.failedFuture(new IllegalStateException("Missing dimension types to create world")); + } + FoliaWorldsLink link = FoliaWorldsLink.get(); if (link.isActive()) { CompletableFuture future = link.createWorld(c); @@ -119,8 +123,6 @@ public interface INMSBinding { int countCustomBiomes(); - void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk); - default boolean supportsDataPacks() { return false; } diff --git a/core/src/main/java/art/arcane/iris/core/nms/datapack/v1206/DataFixerV1206.java b/core/src/main/java/art/arcane/iris/core/nms/datapack/v1206/DataFixerV1206.java index a49ccbd34..3d199e7d6 100644 --- a/core/src/main/java/art/arcane/iris/core/nms/datapack/v1206/DataFixerV1206.java +++ b/core/src/main/java/art/arcane/iris/core/nms/datapack/v1206/DataFixerV1206.java @@ -1,5 +1,6 @@ package art.arcane.iris.core.nms.datapack.v1206; +import art.arcane.iris.Iris; import art.arcane.iris.core.nms.datapack.v1192.DataFixerV1192; import art.arcane.iris.engine.object.IrisBiomeCustom; import art.arcane.iris.engine.object.IrisBiomeCustomSpawn; @@ -7,6 +8,8 @@ import art.arcane.iris.engine.object.IrisBiomeCustomSpawnType; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.json.JSONArray; import art.arcane.volmlib.util.json.JSONObject; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.EntityType; import java.util.Locale; @@ -26,9 +29,23 @@ public class DataFixerV1206 extends DataFixerV1192 { KMap groups = new KMap<>(); for (IrisBiomeCustomSpawn i : spawns) { - JSONArray g = groups.computeIfAbsent(i.getGroup(), (k) -> new JSONArray()); + if (i == null) { + continue; + } + EntityType type = i.getType(); + if (type == null) { + Iris.warn("Skipping custom biome spawn with null entity type in biome " + biome.getId()); + continue; + } + IrisBiomeCustomSpawnType group = i.getGroup() == null ? IrisBiomeCustomSpawnType.MISC : i.getGroup(); + JSONArray g = groups.computeIfAbsent(group, (k) -> new JSONArray()); JSONObject o = new JSONObject(); - o.put("type", i.getType().getKey()); + NamespacedKey key = type.getKey(); + if (key == null) { + Iris.warn("Skipping custom biome spawn with unresolved entity key in biome " + biome.getId()); + continue; + } + o.put("type", key.toString()); o.put("weight", i.getWeight()); o.put("minCount", i.getMinCount()); o.put("maxCount", i.getMaxCount()); diff --git a/core/src/main/java/art/arcane/iris/core/nms/datapack/v1217/DataFixerV1217.java b/core/src/main/java/art/arcane/iris/core/nms/datapack/v1217/DataFixerV1217.java index 8f0e946a2..48cafda41 100644 --- a/core/src/main/java/art/arcane/iris/core/nms/datapack/v1217/DataFixerV1217.java +++ b/core/src/main/java/art/arcane/iris/core/nms/datapack/v1217/DataFixerV1217.java @@ -115,7 +115,7 @@ public class DataFixerV1217 extends DataFixerV1213 { attributes.put("minecraft:gameplay/fast_lava", true); attributes.put("minecraft:gameplay/snow_golem_melts", true); attributes.put("minecraft:visual/default_dripstone_particle", new JSONObject() - .put("value", "minecraft:dripstone_drip_water_lava")); + .put("type", "minecraft:dripping_dripstone_lava")); } if ((Boolean) json.remove("bed_works")) { @@ -132,7 +132,7 @@ public class DataFixerV1217 extends DataFixerV1213 { } attributes.put("minecraft:gameplay/respawn_anchor_works", json.remove("respawn_anchor_works")); - attributes.put("minecraft:gameplay/piglins_zombify", json.remove("piglin_safe")); + attributes.put("minecraft:gameplay/piglins_zombify", !(Boolean) json.remove("piglin_safe")); attributes.put("minecraft:gameplay/can_start_raid", json.remove("has_raids")); var cloud_height = json.remove("cloud_height"); diff --git a/core/src/main/java/art/arcane/iris/core/nms/v1X/NMSBinding1X.java b/core/src/main/java/art/arcane/iris/core/nms/v1X/NMSBinding1X.java index 80cfc281e..8224e6fe1 100644 --- a/core/src/main/java/art/arcane/iris/core/nms/v1X/NMSBinding1X.java +++ b/core/src/main/java/art/arcane/iris/core/nms/v1X/NMSBinding1X.java @@ -40,7 +40,6 @@ import org.bukkit.block.Biome; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.event.entity.CreatureSpawnEvent; -import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.structure.Structure; import org.bukkit.inventory.ItemStack; @@ -118,8 +117,8 @@ public class NMSBinding1X implements INMSBinding { @Override public KList getStructureKeys() { - var list = StreamSupport.stream(Registry.STRUCTURE.spliterator(), false) - .map(Structure::getKey) + List list = StreamSupport.stream(Registry.STRUCTURE.spliterator(), false) + .map(Structure::getKeyOrThrow) .map(NamespacedKey::toString) .toList(); return new KList<>(list); @@ -225,7 +224,11 @@ public class NMSBinding1X implements INMSBinding { @Override public KList getBiomes() { - return new KList<>(Biome.values()).qdel(Biome.CUSTOM); + KList biomes = new KList<>(); + for (Biome biome : Registry.BIOME) { + biomes.add(biome); + } + return biomes; } @Override @@ -240,7 +243,9 @@ public class NMSBinding1X implements INMSBinding { @Override public int getBiomeId(Biome biome) { - return biome.ordinal(); + List biomes = StreamSupport.stream(Registry.BIOME.spliterator(), false).toList(); + int index = biomes.indexOf(biome); + return Math.max(index, 0); } @Override @@ -262,11 +267,6 @@ public class NMSBinding1X implements INMSBinding { return 0; } - @Override - public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) { - - } - @Override public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { return null; diff --git a/core/src/main/java/art/arcane/iris/core/pregenerator/LazyPregenerator.java b/core/src/main/java/art/arcane/iris/core/pregenerator/LazyPregenerator.java index baa682abf..57b8ac822 100644 --- a/core/src/main/java/art/arcane/iris/core/pregenerator/LazyPregenerator.java +++ b/core/src/main/java/art/arcane/iris/core/pregenerator/LazyPregenerator.java @@ -123,13 +123,11 @@ public class LazyPregenerator extends Thread implements Listener { if (lazyGeneratedChunks.get() >= lazyTotalChunks.get()) { if (job.isHealing()) { - int pos = (job.getHealingPosition() + 1) % maxPosition; - job.setHealingPosition(pos); - tickRegenerate(getChunk(pos)); - } else { - Iris.info("Completed Lazy Gen!"); - interrupt(); + Iris.warn("LazyGen healing mode is not supported on 1.21.11; ending lazy generation for " + world.getName() + "."); + job.setHealing(false); } + Iris.info("Completed Lazy Gen!"); + interrupt(); } else { int pos = job.getPosition() + 1; job.setPosition(pos); @@ -172,11 +170,6 @@ public class LazyPregenerator extends Thread implements Listener { }); } - private void tickRegenerate(Position2 chunk) { - J.s(() -> world.regenerateChunk(chunk.getX(), chunk.getZ())); - Iris.verbose("Regenerated " + chunk); - } - public Position2 getChunk(int position) { int p = -1; AtomicInteger xx = new AtomicInteger(); diff --git a/core/src/main/java/art/arcane/iris/core/project/SchemaBuilder.java b/core/src/main/java/art/arcane/iris/core/project/SchemaBuilder.java index fa8b2f649..875e984d1 100644 --- a/core/src/main/java/art/arcane/iris/core/project/SchemaBuilder.java +++ b/core/src/main/java/art/arcane/iris/core/project/SchemaBuilder.java @@ -32,6 +32,8 @@ import art.arcane.iris.util.common.data.B; import art.arcane.volmlib.util.json.JSONArray; import art.arcane.volmlib.util.json.JSONObject; import art.arcane.iris.util.common.reflect.KeyedType; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.enchantments.Enchantment; import org.bukkit.potion.PotionEffectType; import org.jetbrains.annotations.NotNull; @@ -41,6 +43,7 @@ import java.lang.reflect.Field; import java.lang.reflect.InaccessibleObjectException; import java.lang.reflect.Modifier; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -65,8 +68,11 @@ public class SchemaBuilder { private static JSONArray getPotionTypes() { JSONArray a = new JSONArray(); - for (PotionEffectType gg : PotionEffectType.values()) { - a.put(gg.getName().toUpperCase().replaceAll("\\Q \\E", "_")); + for (PotionEffectType gg : Registry.EFFECT) { + NamespacedKey key = KeyedType.getKey(gg); + if (key != null) { + a.put(key.getKey().toUpperCase(Locale.ROOT).replaceAll("\\Q \\E", "_")); + } } return a; @@ -74,8 +80,11 @@ public class SchemaBuilder { private static JSONArray getEnchantTypes() { JSONArray array = new JSONArray(); - for (Enchantment e : Enchantment.values()) { - array.put(e.getKey().getKey()); + for (Enchantment e : Registry.ENCHANTMENT) { + NamespacedKey key = KeyedType.getKey(e); + if (key != null) { + array.put(key.getKey()); + } } return array; } @@ -602,7 +611,7 @@ public class SchemaBuilder { try { k.setAccessible(true); - Object value = k.get(cl.newInstance()); + Object value = k.get(cl.getDeclaredConstructor().newInstance()); if (value != null) { if (present) d.add(" "); diff --git a/core/src/main/java/art/arcane/iris/core/service/BoardSVC.java b/core/src/main/java/art/arcane/iris/core/service/BoardSVC.java index 38ae7f571..19efee832 100644 --- a/core/src/main/java/art/arcane/iris/core/service/BoardSVC.java +++ b/core/src/main/java/art/arcane/iris/core/service/BoardSVC.java @@ -31,6 +31,7 @@ import art.arcane.iris.util.common.format.C; import art.arcane.volmlib.util.format.Form; import art.arcane.iris.util.common.plugin.IrisService; import art.arcane.iris.util.common.scheduling.J; +import art.arcane.volmlib.util.matter.MatterCavern; import lombok.Data; import org.bukkit.Location; import org.bukkit.World; @@ -201,7 +202,7 @@ public class BoardSVC implements IrisService, BoardProvider { lines.add(C.AQUA + "Mantle" + C.GRAY + ": " + engine.getMantle().getLoadedRegionCount()); if (IrisSettings.get().getGeneral().debug) { - lines.add(C.LIGHT_PURPLE + "Carving" + C.GRAY + ": " + engine.getMantle().isCarved(x,y,z)); + lines.add(C.LIGHT_PURPLE + "Carving" + C.GRAY + ": " + (engine.getMantle().getMantle().get(x, y, z, MatterCavern.class) != null)); } lines.add("&7&m "); diff --git a/core/src/main/java/art/arcane/iris/core/service/EditSVC.java b/core/src/main/java/art/arcane/iris/core/service/EditSVC.java index 3fde24912..c7ae592da 100644 --- a/core/src/main/java/art/arcane/iris/core/service/EditSVC.java +++ b/core/src/main/java/art/arcane/iris/core/service/EditSVC.java @@ -48,6 +48,9 @@ public class EditSVC implements IrisService { J.csr(updateTaskId); updateTaskId = -1; } + if (editors == null) { + return; + } flushNow(); } @@ -77,6 +80,9 @@ public class EditSVC implements IrisService { @EventHandler public void on(WorldUnloadEvent e) { + if (editors == null) { + return; + } if (editors.containsKey(e.getWorld()) && !deletingWorld) { editors.remove(e.getWorld()).close(); } @@ -84,6 +90,9 @@ public class EditSVC implements IrisService { public void update() { + if (editors == null) { + return; + } for (World i : editors.k()) { if (M.ms() - editors.get(i).last() > 1000) { editors.remove(i).close(); @@ -92,12 +101,18 @@ public class EditSVC implements IrisService { } public void flushNow() { + if (editors == null) { + return; + } for (World i : editors.k()) { editors.remove(i).close(); } } public BlockEditor open(World world) { + if (editors == null) { + editors = new KMap<>(); + } if (editors.containsKey(world)) { return editors.get(world); } diff --git a/core/src/main/java/art/arcane/iris/core/service/IrisEngineSVC.java b/core/src/main/java/art/arcane/iris/core/service/IrisEngineSVC.java index f481a63a1..3499554db 100644 --- a/core/src/main/java/art/arcane/iris/core/service/IrisEngineSVC.java +++ b/core/src/main/java/art/arcane/iris/core/service/IrisEngineSVC.java @@ -61,8 +61,12 @@ public class IrisEngineSVC implements IrisService { @Override public void onDisable() { - service.shutdown(); - updateTicker.interrupt(); + if (service != null) { + service.shutdown(); + } + if (updateTicker != null) { + updateTicker.interrupt(); + } worlds.keySet().forEach(this::remove); worlds.clear(); } diff --git a/core/src/main/java/art/arcane/iris/core/service/PreservationSVC.java b/core/src/main/java/art/arcane/iris/core/service/PreservationSVC.java index 82f91645b..1ac68de19 100644 --- a/core/src/main/java/art/arcane/iris/core/service/PreservationSVC.java +++ b/core/src/main/java/art/arcane/iris/core/service/PreservationSVC.java @@ -91,7 +91,9 @@ public class PreservationSVC implements IrisService { @Override public void onDisable() { - dereferencer.interrupt(); + if (dereferencer != null) { + dereferencer.interrupt(); + } dereference(); postShutdown(() -> { diff --git a/core/src/main/java/art/arcane/iris/core/tools/IrisCreator.java b/core/src/main/java/art/arcane/iris/core/tools/IrisCreator.java index 078a8a248..aefbe9e8f 100644 --- a/core/src/main/java/art/arcane/iris/core/tools/IrisCreator.java +++ b/core/src/main/java/art/arcane/iris/core/tools/IrisCreator.java @@ -40,12 +40,17 @@ import io.papermc.lib.PaperLib; import lombok.Data; import lombok.experimental.Accessors; import org.bukkit.*; +import org.bukkit.block.Block; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import java.io.File; import java.io.IOException; +import java.lang.reflect.Field; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -231,8 +236,8 @@ public class IrisCreator { Iris.linkMultiverseCore.removeFromConfig(world); if (IrisSettings.get().getStudio().isDisableTimeAndWeather()) { - world.setGameRule(GameRule.ADVANCE_WEATHER, false); - world.setGameRule(GameRule.ADVANCE_TIME, false); + setBooleanGameRule(world, false, "ADVANCE_WEATHER", "DO_WEATHER_CYCLE", "WEATHER_CYCLE", "doWeatherCycle", "weatherCycle"); + setBooleanGameRule(world, false, "ADVANCE_TIME", "DO_DAYLIGHT_CYCLE", "DAYLIGHT_CYCLE", "doDaylightCycle", "daylightCycle"); world.setTime(6000); } }; @@ -278,7 +283,7 @@ public class IrisCreator { CompletableFuture locationFuture = J.sfut(() -> { Location spawnLocation = world.getSpawnLocation(); if (spawnLocation != null) { - return spawnLocation; + return spawnLocation.clone(); } int x = 0; @@ -292,7 +297,8 @@ public class IrisCreator { } try { - return locationFuture.get(15, TimeUnit.SECONDS); + Location rawLocation = locationFuture.get(15, TimeUnit.SECONDS); + return resolveTopSafeStudioLocation(world, rawLocation); } catch (Throwable e) { Iris.warn("Failed to resolve studio entry location for world \"" + world.getName() + "\"."); Iris.reportError(e); @@ -300,6 +306,105 @@ public class IrisCreator { } } + private Location resolveTopSafeStudioLocation(World world, Location rawLocation) { + if (world == null || rawLocation == null) { + return rawLocation; + } + + int chunkX = rawLocation.getBlockX() >> 4; + int chunkZ = rawLocation.getBlockZ() >> 4; + try { + CompletableFuture chunkFuture = PaperLib.getChunkAtAsync(world, chunkX, chunkZ, true); + if (chunkFuture != null) { + chunkFuture.get(15, TimeUnit.SECONDS); + } + } catch (Throwable ignored) { + } + + CompletableFuture regionFuture = new CompletableFuture<>(); + boolean scheduled = J.runRegion(world, chunkX, chunkZ, () -> { + try { + regionFuture.complete(findTopSafeStudioLocation(world, rawLocation)); + } catch (Throwable e) { + regionFuture.completeExceptionally(e); + } + }); + if (!scheduled) { + return rawLocation; + } + + try { + Location resolved = regionFuture.get(15, TimeUnit.SECONDS); + return resolved == null ? rawLocation : resolved; + } catch (Throwable e) { + Iris.warn("Failed to resolve safe studio entry surface for world \"" + world.getName() + "\"."); + Iris.reportError(e); + return rawLocation; + } + } + + private Location findTopSafeStudioLocation(World world, Location source) { + int x = source.getBlockX(); + int z = source.getBlockZ(); + int minY = world.getMinHeight() + 1; + int maxY = world.getMaxHeight() - 2; + int topY = world.getHighestBlockYAt(x, z, HeightMap.MOTION_BLOCKING_NO_LEAVES); + int startY = Math.max(minY, Math.min(maxY, topY + 1)); + float yaw = source.getYaw(); + float pitch = source.getPitch(); + + int upperBound = Math.min(maxY, startY + 16); + for (int y = startY; y <= upperBound; y++) { + if (isSafeStandingLocation(world, x, y, z)) { + return new Location(world, x + 0.5D, y, z + 0.5D, yaw, pitch); + } + } + + int lowerBound = Math.max(minY, startY - 24); + for (int y = startY - 1; y >= lowerBound; y--) { + if (isSafeStandingLocation(world, x, y, z)) { + return new Location(world, x + 0.5D, y, z + 0.5D, yaw, pitch); + } + } + + int fallbackY = Math.max(minY, Math.min(maxY, source.getBlockY())); + return new Location(world, x + 0.5D, fallbackY, z + 0.5D, yaw, pitch); + } + + private boolean isSafeStandingLocation(World world, int x, int y, int z) { + if (y <= world.getMinHeight() || y >= world.getMaxHeight() - 1) { + return false; + } + + Block below = world.getBlockAt(x, y - 1, z); + Block feet = world.getBlockAt(x, y, z); + Block head = world.getBlockAt(x, y + 1, z); + + Material belowType = below.getType(); + if (!belowType.isSolid()) { + return false; + } + if (Tag.LEAVES.isTagged(belowType)) { + return false; + } + if (belowType == Material.LAVA + || belowType == Material.MAGMA_BLOCK + || belowType == Material.FIRE + || belowType == Material.SOUL_FIRE + || belowType == Material.CAMPFIRE + || belowType == Material.SOUL_CAMPFIRE) { + return false; + } + if (feet.getType().isSolid() || head.getType().isSolid()) { + return false; + } + if (feet.isLiquid() || head.isLiquid()) { + return false; + } + + return true; + } + private static boolean containsCreateWorldUnsupportedOperation(Throwable throwable) { Throwable cursor = throwable; while (cursor != null) { @@ -316,6 +421,138 @@ public class IrisCreator { return false; } + @SuppressWarnings("unchecked") + private static void setBooleanGameRule(World world, boolean value, String... names) { + GameRule gameRule = resolveBooleanGameRule(world, names); + if (gameRule != null) { + world.setGameRule(gameRule, value); + } + } + + @SuppressWarnings("unchecked") + private static GameRule resolveBooleanGameRule(World world, String... names) { + if (world == null || names == null || names.length == 0) { + return null; + } + + Set candidates = buildRuleNameCandidates(names); + for (String name : candidates) { + if (name == null || name.isBlank()) { + continue; + } + + try { + Field field = GameRule.class.getField(name); + Object value = field.get(null); + if (value instanceof GameRule gameRule && Boolean.class.equals(gameRule.getType())) { + return (GameRule) gameRule; + } + } catch (Throwable ignored) { + } + + try { + GameRule byName = GameRule.getByName(name); + if (byName != null && Boolean.class.equals(byName.getType())) { + return (GameRule) byName; + } + } catch (Throwable ignored) { + } + } + + String[] availableRules = world.getGameRules(); + if (availableRules == null || availableRules.length == 0) { + return null; + } + + Set normalizedCandidates = new LinkedHashSet<>(); + for (String candidate : candidates) { + if (candidate != null && !candidate.isBlank()) { + normalizedCandidates.add(normalizeRuleName(candidate)); + } + } + + for (String availableRule : availableRules) { + String normalizedAvailable = normalizeRuleName(availableRule); + if (!normalizedCandidates.contains(normalizedAvailable)) { + continue; + } + + try { + GameRule byName = GameRule.getByName(availableRule); + if (byName != null && Boolean.class.equals(byName.getType())) { + return (GameRule) byName; + } + } catch (Throwable ignored) { + } + } + + return null; + } + + private static Set buildRuleNameCandidates(String... names) { + Set candidates = new LinkedHashSet<>(); + for (String name : names) { + if (name == null || name.isBlank()) { + continue; + } + + candidates.add(name); + candidates.add(name.toLowerCase(Locale.ROOT)); + + String lowerCamel = toLowerCamel(name); + if (!lowerCamel.isEmpty()) { + candidates.add(lowerCamel); + } + } + + return candidates; + } + + private static String toLowerCamel(String name) { + if (name == null) { + return ""; + } + + String raw = name.trim(); + if (raw.isEmpty()) { + return ""; + } + + String[] parts = raw.split("_+"); + if (parts.length == 0) { + return raw; + } + + StringBuilder builder = new StringBuilder(); + builder.append(parts[0].toLowerCase(Locale.ROOT)); + for (int i = 1; i < parts.length; i++) { + String part = parts[i].toLowerCase(Locale.ROOT); + if (part.isEmpty()) { + continue; + } + builder.append(Character.toUpperCase(part.charAt(0))); + if (part.length() > 1) { + builder.append(part.substring(1)); + } + } + return builder.toString(); + } + + private static String normalizeRuleName(String name) { + if (name == null || name.isBlank()) { + return ""; + } + + StringBuilder builder = new StringBuilder(name.length()); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if (Character.isLetterOrDigit(c)) { + builder.append(Character.toLowerCase(c)); + } + } + return builder.toString(); + } + private void addToBukkitYml() { YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML); String gen = "Iris:" + dimension; diff --git a/core/src/main/java/art/arcane/iris/engine/data/chunk/LinkedTerrainChunk.java b/core/src/main/java/art/arcane/iris/engine/data/chunk/LinkedTerrainChunk.java index e112eaa55..1e3928166 100644 --- a/core/src/main/java/art/arcane/iris/engine/data/chunk/LinkedTerrainChunk.java +++ b/core/src/main/java/art/arcane/iris/engine/data/chunk/LinkedTerrainChunk.java @@ -18,183 +18,82 @@ package art.arcane.iris.engine.data.chunk; -import art.arcane.iris.core.nms.BiomeBaseInjector; -import art.arcane.iris.core.nms.INMS; -import art.arcane.volmlib.util.data.IrisBiomeStorage; import art.arcane.iris.util.common.data.IrisCustomData; -import lombok.Setter; import org.bukkit.Bukkit; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; -import org.bukkit.generator.ChunkGenerator.BiomeGrid; import org.bukkit.generator.ChunkGenerator.ChunkData; -import org.bukkit.material.MaterialData; -@SuppressWarnings("deprecation") public class LinkedTerrainChunk implements TerrainChunk { - private final IrisBiomeStorage biome3D; - private final BiomeGrid storage; - private ChunkData rawChunkData; - @Setter - private boolean unsafe = true; + private static final int CHUNK_SIZE = 16; + private final ChunkData rawChunkData; + private final int minHeight; + private final int maxHeight; + private final int biomeHeight; + private final Biome[] biomes; public LinkedTerrainChunk(World world) { - this(null, Bukkit.createChunkData(world)); + this(Bukkit.createChunkData(world)); } - public LinkedTerrainChunk(World world, BiomeGrid storage) { - this(storage, Bukkit.createChunkData(world)); - } - - public LinkedTerrainChunk(BiomeGrid storage, ChunkData data) { - this.storage = storage; + public LinkedTerrainChunk(ChunkData data) { rawChunkData = data; - biome3D = storage != null ? null : new IrisBiomeStorage(); + minHeight = data.getMinHeight(); + maxHeight = data.getMaxHeight(); + biomeHeight = Math.max(1, maxHeight - minHeight); + biomes = new Biome[CHUNK_SIZE * biomeHeight * CHUNK_SIZE]; } - @Override - public BiomeBaseInjector getBiomeBaseInjector() { - - if (unsafe) { - return (a, b, c, d) -> { - }; - } - - return (x, y, z, bb) -> INMS.get().forceBiomeInto(x, y, z, bb, storage); - } - - - @Override - public Biome getBiome(int x, int z) { - if (storage != null) { - return storage.getBiome(x, z); - } - - return biome3D.getBiome(x, 0, z); - } - - @Override public Biome getBiome(int x, int y, int z) { - if (storage != null) { - return storage.getBiome(x, y, z); - } - - return biome3D.getBiome(x, y, z); - } - - @Override - public void setBiome(int x, int z, Biome bio) { - if (storage != null) { - storage.setBiome(x, z, bio); - return; - } - - biome3D.setBiome(x, 0, z, bio); - } - - public BiomeGrid getRawBiome() { - return storage; + int index = biomeIndex(x, y, z); + Biome biome = biomes[index]; + return biome == null ? Biome.PLAINS : biome; } @Override public void setBiome(int x, int y, int z, Biome bio) { - if (storage != null) { - storage.setBiome(x, y, z, bio); - return; - } - - biome3D.setBiome(x, y, z, bio); + biomes[biomeIndex(x, y, z)] = bio; } @Override public int getMinHeight() { - return rawChunkData.getMinHeight(); + return minHeight; } @Override public int getMaxHeight() { - return rawChunkData.getMaxHeight(); + return maxHeight; } @Override public synchronized void setBlock(int x, int y, int z, BlockData blockData) { - if (blockData instanceof IrisCustomData d) - blockData = d.getBase(); + if (blockData instanceof IrisCustomData data) { + blockData = data.getBase(); + } rawChunkData.setBlock(x, y, z, blockData); } - - @Override - public BlockData getBlockData(int x, int y, int z) { - return rawChunkData.getBlockData(x, y, z); - } - - @Deprecated - @Override - public synchronized void setBlock(int x, int y, int z, Material material) { - rawChunkData.setBlock(x, y, z, material); - } - - @Deprecated - @Override - public synchronized void setBlock(int x, int y, int z, MaterialData material) { - rawChunkData.setBlock(x, y, z, material); - } - - @Deprecated - @Override - public synchronized void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) { - rawChunkData.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material); - } - - @Deprecated - @Override - public synchronized void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) { - rawChunkData.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material); - } - @Override public synchronized void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) { rawChunkData.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, blockData); } - - @Deprecated @Override - public synchronized Material getType(int x, int y, int z) { - return rawChunkData.getType(x, y, z); - } - - - @Deprecated - @Override - public MaterialData getTypeAndData(int x, int y, int z) { - return rawChunkData.getTypeAndData(x, y, z); - } - - @Deprecated - @Override - public byte getData(int x, int y, int z) { - return rawChunkData.getData(x, y, z); + public BlockData getBlockData(int x, int y, int z) { + return rawChunkData.getBlockData(x, y, z); } @Override - public ChunkData getRaw() { + public ChunkData getChunkData() { return rawChunkData; } - @Override - public void setRaw(ChunkData data) { - rawChunkData = data; - } - - @Override - public void inject(BiomeGrid biome) { - if (biome3D != null) { - biome3D.inject(biome); - } + private int biomeIndex(int x, int y, int z) { + int clampedX = x & (CHUNK_SIZE - 1); + int clampedZ = z & (CHUNK_SIZE - 1); + int clampedY = Math.max(minHeight, Math.min(maxHeight - 1, y)) - minHeight; + return (clampedY * CHUNK_SIZE + clampedZ) * CHUNK_SIZE + clampedX; } } diff --git a/core/src/main/java/art/arcane/iris/engine/data/chunk/MCATerrainChunk.java b/core/src/main/java/art/arcane/iris/engine/data/chunk/MCATerrainChunk.java deleted file mode 100644 index 96113b120..000000000 --- a/core/src/main/java/art/arcane/iris/engine/data/chunk/MCATerrainChunk.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2022 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.engine.data.chunk; - -import art.arcane.iris.Iris; -import art.arcane.iris.core.nms.BiomeBaseInjector; -import art.arcane.iris.util.common.data.IrisCustomData; -import art.arcane.iris.util.nbt.common.mca.Chunk; -import art.arcane.iris.util.nbt.common.mca.NBTWorld; -import lombok.AllArgsConstructor; -import lombok.Builder; -import org.bukkit.Material; -import org.bukkit.block.Biome; -import org.bukkit.block.data.BlockData; -import org.bukkit.generator.ChunkGenerator; -import org.bukkit.material.MaterialData; - -@Builder -@AllArgsConstructor -public class MCATerrainChunk implements TerrainChunk { - private final NBTWorld writer; - private final BiomeBaseInjector injector; - private final int ox; - private final int oz; - private final int minHeight; - private final int maxHeight; - private final Chunk mcaChunk; - - @Override - public BiomeBaseInjector getBiomeBaseInjector() { - return injector; - } - - @Override - public Biome getBiome(int x, int z) { - return Biome.THE_VOID; - } - - @Override - public Biome getBiome(int x, int y, int z) { - return Biome.THE_VOID; - } - - @Override - public void setBiome(int x, int z, Biome bio) { - setBiome(ox + x, 0, oz + z, bio); - } - - @Override - public void setBiome(int x, int y, int z, Biome bio) { - mcaChunk.setBiomeAt((ox + x) & 15, y, (oz + z) & 15, writer.getBiomeId(bio)); - } - - @Override - public int getMinHeight() { - return minHeight; - } - - @Override - public int getMaxHeight() { - return maxHeight; - } - - @Override - public void setBlock(int x, int y, int z, BlockData blockData) { - int xx = (x + ox) & 15; - int zz = (z + oz) & 15; - - if (y > getMaxHeight() || y < getMinHeight()) { - return; - } - - if (blockData == null) { - Iris.error("NULL BD"); - } - if (blockData instanceof IrisCustomData data) - blockData = data.getBase(); - - mcaChunk.setBlockStateAt(xx, y, zz, NBTWorld.getCompound(blockData), false); - } - - @Override - public org.bukkit.block.data.BlockData getBlockData(int x, int y, int z) { - if (y > getMaxHeight()) { - y = getMaxHeight(); - } - - if (y < getMinHeight()) { - y = getMinHeight(); - } - - return NBTWorld.getBlockData(mcaChunk.getBlockStateAt((x + ox) & 15, y, (z + oz) & 15)); - } - - @Override - public ChunkGenerator.ChunkData getRaw() { - return null; - } - - @Override - public void setRaw(ChunkGenerator.ChunkData data) { - - } - - @Override - public void inject(ChunkGenerator.BiomeGrid biome) { - - } - - @Override - public void setBlock(int x, int y, int z, Material material) { - - } - - @Override - public void setBlock(int x, int y, int z, MaterialData material) { - - } - - @Override - public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) { - - } - - @Override - public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) { - - } - - @Override - public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) { - - } - - - @Override - public Material getType(int x, int y, int z) { - return null; - } - - - @Override - public MaterialData getTypeAndData(int x, int y, int z) { - return null; - } - - @Override - public byte getData(int x, int y, int z) { - return 0; - } -} diff --git a/core/src/main/java/art/arcane/iris/engine/data/chunk/TerrainChunk.java b/core/src/main/java/art/arcane/iris/engine/data/chunk/TerrainChunk.java index 581cc806b..8961b8db3 100644 --- a/core/src/main/java/art/arcane/iris/engine/data/chunk/TerrainChunk.java +++ b/core/src/main/java/art/arcane/iris/engine/data/chunk/TerrainChunk.java @@ -18,116 +18,27 @@ package art.arcane.iris.engine.data.chunk; -import art.arcane.iris.core.nms.BiomeBaseInjector; import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; -import org.bukkit.generator.ChunkGenerator.BiomeGrid; import org.bukkit.generator.ChunkGenerator.ChunkData; -public interface TerrainChunk extends BiomeGrid, ChunkData { +public interface TerrainChunk { static TerrainChunk create(World world) { return new LinkedTerrainChunk(world); } - static TerrainChunk create(World world, BiomeGrid grid) { - return new LinkedTerrainChunk(world, grid); + static TerrainChunk create(ChunkData raw) { + return new LinkedTerrainChunk(raw); } - static TerrainChunk createUnsafe(World world, BiomeGrid grid) { - LinkedTerrainChunk ltc = new LinkedTerrainChunk(world, grid); - ltc.setUnsafe(true); - return ltc; - } - - static TerrainChunk create(ChunkData raw, BiomeGrid grid) { - return new LinkedTerrainChunk(grid, raw); - } - - BiomeBaseInjector getBiomeBaseInjector(); - - /** - * Get biome at x, z within chunk being generated - * - * @param x - 0-15 - * @param z - 0-15 - * @return Biome value - * @deprecated biomes are now 3-dimensional - */ - - @Deprecated - Biome getBiome(int x, int z); - - /** - * Get biome at x, z within chunk being generated - * - * @param x - 0-15 - * @param y - 0-255 - * @param z - 0-15 - * @return Biome value - */ Biome getBiome(int x, int y, int z); - /** - * Set biome at x, z within chunk being generated - * - * @param x - 0-15 - * @param z - 0-15 - * @param bio - Biome value - * @deprecated biomes are now 3-dimensional - */ - @Deprecated - void setBiome(int x, int z, Biome bio); - - /** - * Set biome at x, z within chunk being generated - * - * @param x - 0-15 - * @param y - 0-255 - * @param z - 0-15 - * @param bio - Biome value - */ void setBiome(int x, int y, int z, Biome bio); - - /** - * Get the maximum height for the chunk. - *

- * Setting blocks at or above this height will do nothing. - * - * @return the maximum height - */ + int getMinHeight(); int getMaxHeight(); - - /** - * Set the block at x,y,z in the chunk data to material. - *

- * Setting blocks outside the chunk's bounds does nothing. - * - * @param x the x location in the chunk from 0-15 inclusive - * @param y the y location in the chunk from 0 (inclusive) - maxHeight - * (exclusive) - * @param z the z location in the chunk from 0-15 inclusive - * @param blockData the type to set the block to - */ void setBlock(int x, int y, int z, BlockData blockData); - - /** - * Get the type and data of the block at x, y, z. - *

- * Getting blocks outside the chunk's bounds returns air. - * - * @param x the x location in the chunk from 0-15 inclusive - * @param y the y location in the chunk from 0 (inclusive) - maxHeight - * (exclusive) - * @param z the z location in the chunk from 0-15 inclusive - * @return the data of the block or the BlockData for air if x, y or z are - * outside the chunk's bounds - */ + void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData); BlockData getBlockData(int x, int y, int z); - - ChunkData getRaw(); - - void setRaw(ChunkData data); - - void inject(BiomeGrid biome); + ChunkData getChunkData(); } diff --git a/core/src/main/java/art/arcane/iris/engine/decorator/IrisEngineDecorator.java b/core/src/main/java/art/arcane/iris/engine/decorator/IrisEngineDecorator.java index a8893319b..3c6636a15 100644 --- a/core/src/main/java/art/arcane/iris/engine/decorator/IrisEngineDecorator.java +++ b/core/src/main/java/art/arcane/iris/engine/decorator/IrisEngineDecorator.java @@ -22,6 +22,7 @@ import art.arcane.iris.Iris; import art.arcane.iris.engine.framework.Engine; import art.arcane.iris.engine.framework.EngineAssignedComponent; import art.arcane.iris.engine.framework.EngineDecorator; +import art.arcane.iris.engine.mantle.EngineMantle; import art.arcane.iris.engine.object.IrisBiome; import art.arcane.iris.engine.object.IrisDecorationPart; import art.arcane.iris.engine.object.IrisDecorator; @@ -88,7 +89,10 @@ public abstract class IrisEngineDecorator extends EngineAssignedComponent implem continue; int yy = y + f.getModY(); - BlockData r = getEngine().getMantle().get(x + f.getModX(), yy, z + f.getModZ()); + BlockData r = getEngine().getMantle().getMantle().get(x + f.getModX(), yy, z + f.getModZ(), BlockData.class); + if (r == null) { + r = EngineMantle.AIR; + } if (r.isFaceSturdy(f.getOppositeFace(), BlockSupport.FULL)) { found = true; data.setFace(f, true); diff --git a/core/src/main/java/art/arcane/iris/engine/framework/Engine.java b/core/src/main/java/art/arcane/iris/engine/framework/Engine.java index d621bd80d..0faa267f8 100644 --- a/core/src/main/java/art/arcane/iris/engine/framework/Engine.java +++ b/core/src/main/java/art/arcane/iris/engine/framework/Engine.java @@ -62,6 +62,7 @@ import art.arcane.volmlib.util.matter.MatterUpdate; import art.arcane.volmlib.util.matter.slices.container.JigsawPieceContainer; import art.arcane.iris.util.common.parallel.BurstExecutor; import art.arcane.iris.util.common.parallel.MultiBurst; +import art.arcane.iris.util.common.reflect.KeyedType; import art.arcane.iris.util.common.reflect.W; import art.arcane.volmlib.util.scheduling.ChronoLatch; import art.arcane.iris.util.common.scheduling.J; @@ -149,7 +150,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat @BlockCoordinates default void generate(int x, int z, TerrainChunk tc, boolean multicore) throws WrongEngineBroException { - generate(x, z, Hunk.view(tc), Hunk.view(tc, tc.getMinHeight(), tc.getMaxHeight()), multicore); + generate(x, z, Hunk.view(tc), Hunk.viewBiomes(tc), multicore); } @BlockCoordinates @@ -302,8 +303,13 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat chunk.raiseFlagUnchecked(MantleFlag.TILE, run(semaphore, c, () -> { chunk.iterate(TileWrapper.class, (x, y, z, v) -> { Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15); - if (!TileData.setTileState(block, v.getData())) - Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", block.getX(), block.getY(), block.getZ(), block.getType().getKey(), v.getData().getMaterial().getKey()); + if (!TileData.setTileState(block, v.getData())) { + NamespacedKey blockTypeKey = KeyedType.getKey(block.getType()); + NamespacedKey tileTypeKey = KeyedType.getKey(v.getData().getMaterial()); + String blockType = blockTypeKey == null ? block.getType().name() : blockTypeKey.toString(); + String tileType = tileTypeKey == null ? v.getData().getMaterial().name() : tileTypeKey.toString(); + Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", block.getX(), block.getY(), block.getZ(), blockType, tileType); + } }); }, 0)); chunk.raiseFlagUnchecked(MantleFlag.CUSTOM, run(semaphore, c, () -> { diff --git a/core/src/main/java/art/arcane/iris/engine/framework/placer/WorldObjectPlacer.java b/core/src/main/java/art/arcane/iris/engine/framework/placer/WorldObjectPlacer.java index b6d9bcee1..fbc67d2cc 100644 --- a/core/src/main/java/art/arcane/iris/engine/framework/placer/WorldObjectPlacer.java +++ b/core/src/main/java/art/arcane/iris/engine/framework/placer/WorldObjectPlacer.java @@ -15,6 +15,7 @@ import art.arcane.volmlib.util.collection.KList; import art.arcane.iris.util.common.data.B; import art.arcane.iris.util.common.data.IrisCustomData; import art.arcane.volmlib.util.math.RNG; +import art.arcane.volmlib.util.matter.MatterCavern; import lombok.EqualsAndHashCode; import lombok.Getter; import org.bukkit.Bukkit; @@ -97,7 +98,7 @@ public class WorldObjectPlacer implements IObjectPlacer { @Override public boolean isCarved(int x, int y, int z) { - return mantle.isCarved(x, y, z); + return mantle.getMantle().get(x, y, z, MatterCavern.class) != null; } @Override diff --git a/core/src/main/java/art/arcane/iris/engine/jigsaw/PlannedPiece.java b/core/src/main/java/art/arcane/iris/engine/jigsaw/PlannedPiece.java index 62d322ff3..f3f4db674 100644 --- a/core/src/main/java/art/arcane/iris/engine/jigsaw/PlannedPiece.java +++ b/core/src/main/java/art/arcane/iris/engine/jigsaw/PlannedPiece.java @@ -65,14 +65,17 @@ public class PlannedPiece { public PlannedPiece(PlannedStructure structure, IrisPosition position, IrisJigsawPiece piece, IrisObjectRotation rot) { this.structure = structure; this.position = position; - this.data = piece.getLoader(); - this.setRotation(rot); - this.ogObject = data.getObjectLoader().load(piece.getObject()); - this.object = structure.rotated(piece, rotation); - this.piece = rotation.rotateCopy(piece, new IrisPosition(object.getShrinkOffset())); - this.piece.setLoadKey(piece.getLoadKey()); - this.object.setLoadKey(piece.getObject()); - this.ogObject.setLoadKey(piece.getObject()); + this.data = piece.getLoader(); + this.setRotation(rot); + this.ogObject = data.getObjectLoader().load(piece.getObject()); + this.object = structure.rotated(piece, rotation); + if (this.ogObject == null || this.object == null) { + throw new IllegalStateException("Unable to create planned piece for object \"" + piece.getObject() + "\""); + } + this.piece = rotation.rotateCopy(piece, new IrisPosition(object.getShrinkOffset())); + this.piece.setLoadKey(piece.getLoadKey()); + this.object.setLoadKey(piece.getObject()); + this.ogObject.setLoadKey(piece.getObject()); this.connected = new KList<>(); this.realPositions = new KMap<>(); diff --git a/core/src/main/java/art/arcane/iris/engine/jigsaw/PlannedStructure.java b/core/src/main/java/art/arcane/iris/engine/jigsaw/PlannedStructure.java index 508cb361d..55e98e0ec 100644 --- a/core/src/main/java/art/arcane/iris/engine/jigsaw/PlannedStructure.java +++ b/core/src/main/java/art/arcane/iris/engine/jigsaw/PlannedStructure.java @@ -357,6 +357,16 @@ public class PlannedStructure { public IrisObject rotated(IrisJigsawPiece piece, IrisObjectRotation rotation) { String key = piece.getObject() + "-" + rotation.hashCode(); - return objectRotationCache.computeIfAbsent(key, (k) -> rotation.rotateCopy(data.getObjectLoader().load(piece.getObject()))); + return objectRotationCache.computeIfAbsent(key, (k) -> { + IrisObject loaded = data.getObjectLoader().load(piece.getObject()); + if (loaded == null) { + throw new IllegalStateException("Unable to load jigsaw object \"" + piece.getObject() + "\" for piece \"" + piece.getLoadKey() + "\""); + } + IrisObject rotatedObject = rotation.rotateCopy(loaded); + if (rotatedObject == null) { + throw new IllegalStateException("Unable to rotate jigsaw object \"" + piece.getObject() + "\" for piece \"" + piece.getLoadKey() + "\""); + } + return rotatedObject; + }); } } diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisAttributeModifier.java b/core/src/main/java/art/arcane/iris/engine/object/IrisAttributeModifier.java index 46d85a6d0..68d916e71 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisAttributeModifier.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisAttributeModifier.java @@ -24,12 +24,17 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import org.bukkit.NamespacedKey; import org.bukkit.attribute.Attributable; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeModifier; import org.bukkit.attribute.AttributeModifier.Operation; +import org.bukkit.inventory.EquipmentSlotGroup; import org.bukkit.inventory.meta.ItemMeta; +import java.util.Locale; +import java.util.UUID; + @Snippet("attribute-modifier") @Accessors(chain = true) @AllArgsConstructor @@ -62,17 +67,35 @@ public class IrisAttributeModifier { public void apply(RNG rng, ItemMeta meta) { if (rng.nextDouble() < getChance()) { - meta.addAttributeModifier(getAttribute(), new AttributeModifier(getName(), getAmount(rng), getOperation())); + meta.addAttributeModifier(getAttribute(), createModifier(rng)); } } public void apply(RNG rng, Attributable meta) { if (rng.nextDouble() < getChance()) { - meta.getAttribute(getAttribute()).addModifier(new AttributeModifier(getName(), getAmount(rng), getOperation())); + meta.getAttribute(getAttribute()).addModifier(createModifier(rng)); } } public double getAmount(RNG rng) { return rng.d(getMinAmount(), getMaxAmount()); } + + private AttributeModifier createModifier(RNG rng) { + NamespacedKey key = NamespacedKey.minecraft(generateModifierKey()); + return new AttributeModifier(key, getAmount(rng), getOperation(), EquipmentSlotGroup.ANY); + } + + private String generateModifierKey() { + String source = getName() == null ? "modifier" : getName(); + String normalized = source.toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9/._-]", "_"); + if (normalized.isBlank()) { + normalized = "modifier"; + } + if (normalized.length() > 32) { + normalized = normalized.substring(0, 32); + } + String random = UUID.randomUUID().toString().replace("-", ""); + return normalized + "_" + random; + } } diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisBiomeCustom.java b/core/src/main/java/art/arcane/iris/engine/object/IrisBiomeCustom.java index 3a4912a94..9a625c14e 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisBiomeCustom.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisBiomeCustom.java @@ -29,6 +29,8 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.EntityType; import java.awt.*; import java.util.Locale; @@ -142,9 +144,23 @@ public class IrisBiomeCustom { KMap groups = new KMap<>(); for (IrisBiomeCustomSpawn i : getSpawns()) { - JSONArray g = groups.computeIfAbsent(i.getGroup(), (k) -> new JSONArray()); + if (i == null) { + continue; + } + EntityType type = i.getType(); + if (type == null) { + Iris.warn("Skipping custom biome spawn with null entity type in biome " + getId()); + continue; + } + IrisBiomeCustomSpawnType group = i.getGroup() == null ? IrisBiomeCustomSpawnType.MISC : i.getGroup(); + JSONArray g = groups.computeIfAbsent(group, (k) -> new JSONArray()); JSONObject o = new JSONObject(); - o.put("type", "minecraft:" + i.getType().name().toLowerCase()); + NamespacedKey key = type.getKey(); + if (key == null) { + Iris.warn("Skipping custom biome spawn with unresolved entity key in biome " + getId()); + continue; + } + o.put("type", key.toString()); o.put("weight", i.getWeight()); o.put("minCount", i.getMinCount()); o.put("maxCount", i.getMaxCount()); diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisEffect.java b/core/src/main/java/art/arcane/iris/engine/object/IrisEffect.java index cc22bd237..e263d2b2b 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisEffect.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisEffect.java @@ -24,13 +24,16 @@ import art.arcane.iris.engine.framework.Engine; import art.arcane.iris.engine.object.annotations.*; import art.arcane.volmlib.util.math.RNG; import art.arcane.volmlib.util.scheduling.ChronoLatch; +import art.arcane.iris.util.common.reflect.KeyedType; import art.arcane.iris.util.common.scheduling.J; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import org.bukkit.Location; +import org.bukkit.NamespacedKey; import org.bukkit.Particle; +import org.bukkit.Registry; import org.bukkit.Sound; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -38,6 +41,8 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; +import java.util.Locale; + @Snippet("effect") @Accessors(chain = true) @NoArgsConstructor @@ -164,8 +169,9 @@ public class IrisEffect { } try { - for (PotionEffectType i : PotionEffectType.values()) { - if (i.getName().toUpperCase().replaceAll("\\Q \\E", "_").equals(getPotionEffect())) { + for (PotionEffectType i : Registry.EFFECT) { + NamespacedKey key = KeyedType.getKey(i); + if (key != null && key.getKey().toUpperCase(Locale.ROOT).replaceAll("\\Q \\E", "_").equals(getPotionEffect())) { t = i; return t; diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisEnchantment.java b/core/src/main/java/art/arcane/iris/engine/object/IrisEnchantment.java index cb248b654..892858b9a 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisEnchantment.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisEnchantment.java @@ -26,6 +26,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.bukkit.inventory.meta.ItemMeta; @@ -58,7 +59,7 @@ public class IrisEnchantment { public void apply(RNG rng, ItemMeta meta) { try { - Enchantment enchant = Enchantment.getByKey(NamespacedKey.minecraft(getEnchantment())); + Enchantment enchant = Registry.ENCHANTMENT.get(NamespacedKey.minecraft(getEnchantment())); if (enchant == null) { Iris.warn("Unknown Enchantment: " + getEnchantment()); return; diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisEntity.java b/core/src/main/java/art/arcane/iris/engine/object/IrisEntity.java index 2a0b4ac81..035ee9179 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisEntity.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisEntity.java @@ -423,7 +423,11 @@ public class IrisEntity extends IrisRegistrant { return null; } - if (type.equals(EntityType.UNKNOWN) && !isSpecialType()) { + if (type == null) { + return null; + } + + if (EntityType.UNKNOWN.equals(type) && !isSpecialType()) { return null; } diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisLoot.java b/core/src/main/java/art/arcane/iris/engine/object/IrisLoot.java index 3fbc927d4..dcb54de31 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisLoot.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisLoot.java @@ -37,15 +37,17 @@ import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import org.bukkit.DyeColor; import org.bukkit.Material; -import org.bukkit.inventory.ItemFlag; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.Damageable; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.LeatherArmorMeta; -import org.bukkit.material.Colorable; - -import java.awt.*; -import java.util.Optional; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.LeatherArmorMeta; +import org.bukkit.inventory.meta.components.CustomModelDataComponent; +import org.bukkit.material.Colorable; + +import java.awt.*; +import java.util.List; +import java.util.Optional; @Snippet("loot") @Accessors(chain = true) @@ -176,9 +178,11 @@ public class IrisLoot { m.addItemFlags(i); } - if (getCustomModel() != null) { - m.setCustomModelData(getCustomModel()); - } + if (getCustomModel() != null) { + CustomModelDataComponent customModelData = m.getCustomModelDataComponent(); + customModelData.setFloats(List.of(getCustomModel().floatValue())); + m.setCustomModelDataComponent(customModelData); + } if (is.getType().getMaxDurability() > 0 && m instanceof Damageable d) { int max = is.getType().getMaxDurability(); @@ -194,10 +198,9 @@ public class IrisLoot { colorable.setColor(getDyeColor()); } - if (displayName != null) { - m.setLocalizedName(C.translateAlternateColorCodes('&', displayName)); - m.setDisplayName(C.translateAlternateColorCodes('&', displayName)); - } + if (displayName != null) { + m.setDisplayName(C.translateAlternateColorCodes('&', displayName)); + } KList lore = new KList<>(); diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisPotionEffect.java b/core/src/main/java/art/arcane/iris/engine/object/IrisPotionEffect.java index 0de06ed91..ab736f06d 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisPotionEffect.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisPotionEffect.java @@ -21,14 +21,19 @@ package art.arcane.iris.engine.object; import art.arcane.iris.Iris; import art.arcane.iris.engine.data.cache.AtomicCache; import art.arcane.iris.engine.object.annotations.*; +import art.arcane.iris.util.common.reflect.KeyedType; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import org.bukkit.NamespacedKey; import org.bukkit.entity.LivingEntity; +import org.bukkit.Registry; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; +import java.util.Locale; + @Accessors(chain = true) @NoArgsConstructor @AllArgsConstructor @@ -65,8 +70,9 @@ public class IrisPotionEffect { } try { - for (PotionEffectType i : PotionEffectType.values()) { - if (i.getName().toUpperCase().replaceAll("\\Q \\E", "_").equals(getPotionEffect())) { + for (PotionEffectType i : Registry.EFFECT) { + NamespacedKey key = KeyedType.getKey(i); + if (key != null && key.getKey().toUpperCase(Locale.ROOT).replaceAll("\\Q \\E", "_").equals(getPotionEffect())) { t = i; return t; diff --git a/core/src/main/java/art/arcane/iris/engine/object/LegacyTileData.java b/core/src/main/java/art/arcane/iris/engine/object/LegacyTileData.java index c0951bd61..e8fd3a94c 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/LegacyTileData.java +++ b/core/src/main/java/art/arcane/iris/engine/object/LegacyTileData.java @@ -4,6 +4,7 @@ import art.arcane.iris.core.nms.container.Pair; import art.arcane.iris.engine.data.cache.AtomicCache; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; +import art.arcane.iris.util.common.reflect.KeyedType; import art.arcane.iris.util.common.scheduling.J; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; @@ -14,6 +15,8 @@ import org.bukkit.*; import org.bukkit.block.*; import org.bukkit.block.banner.Pattern; import org.bukkit.block.banner.PatternType; +import org.bukkit.block.sign.Side; +import org.bukkit.block.sign.SignSide; import org.bukkit.block.data.BlockData; import org.bukkit.entity.EntityType; import org.jetbrains.annotations.NotNull; @@ -22,6 +25,7 @@ import org.jetbrains.annotations.Nullable; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -126,11 +130,11 @@ public class LegacyTileData extends TileData { dyeColor = DyeColor.values()[in.readByte()]; } - @SuppressWarnings("deprecation") private static SignHandler fromBukkit(BlockState blockState, Material type) { if (!signsTag().isTagged(type) || !(blockState instanceof Sign sign)) return null; - return new SignHandler(sign.getLine(0), sign.getLine(1), sign.getLine(2), sign.getLine(3), sign.getColor()); + SignSide front = sign.getSide(Side.FRONT); + return new SignHandler(front.getLine(0), front.getLine(1), front.getLine(2), front.getLine(3), front.getColor()); } @Override @@ -155,11 +159,18 @@ public class LegacyTileData extends TileData { @Override public void toBukkit(Block block) { Sign sign = (Sign) block.getState(); - sign.setLine(0, line1); - sign.setLine(1, line2); - sign.setLine(2, line3); - sign.setLine(3, line4); - sign.setColor(dyeColor); + SignSide front = sign.getSide(Side.FRONT); + SignSide back = sign.getSide(Side.BACK); + front.setLine(0, line1); + front.setLine(1, line2); + front.setLine(2, line3); + front.setLine(3, line4); + front.setColor(dyeColor); + back.setLine(0, line1); + back.setLine(1, line2); + back.setLine(2, line3); + back.setLine(3, line4); + back.setColor(dyeColor); sign.update(); } } @@ -170,7 +181,33 @@ public class LegacyTileData extends TileData { private final EntityType type; private SpawnerHandler(DataInputStream in) throws IOException { - type = EntityType.values()[in.readShort()]; + EntityType resolved = null; + if (in.markSupported()) { + in.mark(Integer.MAX_VALUE); + } + + try { + String keyString = in.readUTF(); + NamespacedKey key = NamespacedKey.fromString(keyString); + resolved = key == null ? null : Registry.ENTITY_TYPE.get(key); + if (resolved == null && in.markSupported()) { + in.reset(); + } + } catch (Throwable ignored) { + if (in.markSupported()) { + in.reset(); + } + } + + if (resolved == null) { + short legacyOrdinal = in.readShort(); + EntityType[] legacyValues = EntityType.values(); + if (legacyOrdinal >= 0 && legacyOrdinal < legacyValues.length) { + resolved = legacyValues[legacyOrdinal]; + } + } + + type = resolved == null ? EntityType.PIG : resolved; } private static SpawnerHandler fromBukkit(BlockState blockState, Material material) { @@ -191,7 +228,8 @@ public class LegacyTileData extends TileData { @Override public void toBinary(DataOutputStream out) throws IOException { - out.writeShort(type.ordinal()); + NamespacedKey key = KeyedType.getKey(type); + out.writeUTF(key == null ? type.name() : key.toString()); } @Override @@ -209,13 +247,55 @@ public class LegacyTileData extends TileData { private final DyeColor baseColor; private BannerHandler(DataInputStream in) throws IOException { - baseColor = DyeColor.values()[in.readByte()]; + DyeColor[] dyeColors = DyeColor.values(); + int baseColorIndex = in.readUnsignedByte(); + baseColor = baseColorIndex >= 0 && baseColorIndex < dyeColors.length ? dyeColors[baseColorIndex] : DyeColor.WHITE; patterns = new KList<>(); - int listSize = in.readByte(); + int listSize = in.readUnsignedByte(); + + if (in.markSupported()) { + in.mark(Integer.MAX_VALUE); + } + + boolean parsedKeyed = false; + try { + KList keyedPatterns = new KList<>(); + for (int i = 0; i < listSize; i++) { + int colorIndex = in.readUnsignedByte(); + DyeColor color = colorIndex >= 0 && colorIndex < dyeColors.length ? dyeColors[colorIndex] : DyeColor.WHITE; + NamespacedKey patternKey = NamespacedKey.fromString(in.readUTF()); + PatternType pattern = patternKey == null ? null : Registry.BANNER_PATTERN.get(patternKey); + if (pattern == null) { + throw new IOException("Unknown banner pattern key"); + } + keyedPatterns.add(new Pattern(color, pattern)); + } + patterns.addAll(keyedPatterns); + parsedKeyed = true; + } catch (Throwable ignored) { + if (in.markSupported()) { + in.reset(); + } + } + + if (parsedKeyed) { + return; + } + + PatternType[] legacyPatternTypes = PatternType.values(); + PatternType fallbackPattern = Registry.BANNER_PATTERN.get(NamespacedKey.minecraft("base")); + if (fallbackPattern == null && legacyPatternTypes.length > 0) { + fallbackPattern = legacyPatternTypes[0]; + } + for (int i = 0; i < listSize; i++) { - DyeColor color = DyeColor.values()[in.readByte()]; - PatternType pattern = PatternType.values()[in.readByte()]; - patterns.add(new Pattern(color, pattern)); + int colorIndex = in.readUnsignedByte(); + DyeColor color = colorIndex >= 0 && colorIndex < dyeColors.length ? dyeColors[colorIndex] : DyeColor.WHITE; + int legacyPatternIndex = in.readUnsignedByte(); + PatternType pattern = legacyPatternIndex >= 0 && legacyPatternIndex < legacyPatternTypes.length ? legacyPatternTypes[legacyPatternIndex] : fallbackPattern; + if (pattern != null) { + patterns.add(new Pattern(color, pattern)); + } } } @@ -241,7 +321,11 @@ public class LegacyTileData extends TileData { out.writeByte(patterns.size()); for (Pattern i : patterns) { out.writeByte(i.getColor().ordinal()); - out.writeByte(i.getPattern().ordinal()); + NamespacedKey key = KeyedType.getKey(i.getPattern()); + if (key == null) { + key = NamespacedKey.minecraft("base"); + } + out.writeUTF(key.toString()); } } @@ -262,7 +346,11 @@ public class LegacyTileData extends TileData { return new Tag<>() { @Override public boolean isTagged(@NotNull Material item) { - return item.getKey().getKey().endsWith("_sign"); + NamespacedKey key = KeyedType.getKey(item); + if (key != null) { + return key.getKey().endsWith("_sign"); + } + return item.name().toLowerCase(Locale.ROOT).endsWith("_sign"); } @NotNull diff --git a/core/src/main/java/art/arcane/iris/engine/object/TileData.java b/core/src/main/java/art/arcane/iris/engine/object/TileData.java index 444f0703c..ad5679e9f 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/TileData.java +++ b/core/src/main/java/art/arcane/iris/engine/object/TileData.java @@ -20,11 +20,14 @@ package art.arcane.iris.engine.object; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.Strictness; import art.arcane.iris.Iris; import art.arcane.iris.core.nms.INMS; +import art.arcane.iris.util.common.reflect.KeyedType; import art.arcane.volmlib.util.collection.KMap; import lombok.*; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.block.Block; import org.bukkit.block.TileState; import org.bukkit.block.data.BlockData; @@ -39,7 +42,7 @@ import java.io.IOException; @AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) public class TileData implements Cloneable { - private static final Gson gson = new GsonBuilder().disableHtmlEscaping().setLenient().create(); + private static final Gson gson = new GsonBuilder().disableHtmlEscaping().setStrictness(Strictness.LENIENT).create(); @NonNull private Material material; @@ -125,7 +128,13 @@ public class TileData implements Cloneable { } public void toBinary(DataOutputStream out) throws IOException { - out.writeUTF(material == null ? "" : material.getKey().toString()); + if (material == null) { + out.writeUTF(""); + } else { + NamespacedKey key = KeyedType.getKey(material); + String value = key == null ? material.name() : key.toString(); + out.writeUTF(value); + } out.writeUTF(gson.toJson(properties)); } @@ -139,6 +148,8 @@ public class TileData implements Cloneable { @Override public String toString() { - return material.getKey() + gson.toJson(properties); + NamespacedKey key = KeyedType.getKey(material); + String value = key == null ? String.valueOf(material) : key.toString(); + return value + gson.toJson(properties); } } diff --git a/core/src/main/java/art/arcane/iris/engine/object/annotations/functions/LootTableKeyFunction.java b/core/src/main/java/art/arcane/iris/engine/object/annotations/functions/LootTableKeyFunction.java index a10bf201f..138f13c4b 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/annotations/functions/LootTableKeyFunction.java +++ b/core/src/main/java/art/arcane/iris/engine/object/annotations/functions/LootTableKeyFunction.java @@ -4,12 +4,11 @@ import art.arcane.iris.core.loader.IrisData; import art.arcane.iris.engine.framework.ListFunction; import art.arcane.volmlib.util.collection.KList; import org.bukkit.NamespacedKey; -import org.bukkit.Registry; import org.bukkit.loot.LootTable; import org.bukkit.loot.LootTables; +import java.util.Arrays; import java.util.stream.Collectors; -import java.util.stream.StreamSupport; public class LootTableKeyFunction implements ListFunction> { @Override @@ -24,7 +23,7 @@ public class LootTableKeyFunction implements ListFunction> { @Override public KList apply(IrisData data) { - return StreamSupport.stream(Registry.LOOT_TABLES.spliterator(), false) + return Arrays.stream(LootTables.values()) .map(LootTables::getLootTable) .map(LootTable::getKey) .map(NamespacedKey::toString) diff --git a/core/src/main/java/art/arcane/iris/engine/platform/BukkitChunkGenerator.java b/core/src/main/java/art/arcane/iris/engine/platform/BukkitChunkGenerator.java index 53b23c796..bedfd94a7 100644 --- a/core/src/main/java/art/arcane/iris/engine/platform/BukkitChunkGenerator.java +++ b/core/src/main/java/art/arcane/iris/engine/platform/BukkitChunkGenerator.java @@ -35,8 +35,7 @@ import art.arcane.iris.engine.object.IrisWorld; import art.arcane.iris.engine.object.StudioMode; import art.arcane.iris.engine.platform.studio.StudioGenerator; import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.data.IrisBiomeStorage; -import art.arcane.iris.util.project.hunk.view.BiomeGridHunkHolder; +import art.arcane.iris.util.project.hunk.Hunk; import art.arcane.iris.util.project.hunk.view.ChunkDataHunkHolder; import art.arcane.volmlib.util.io.ReactiveFolder; import art.arcane.volmlib.util.scheduling.ChronoLatch; @@ -47,6 +46,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Setter; import org.bukkit.*; +import org.bukkit.block.Biome; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -220,8 +220,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun if (acquireWait >= 5000L) { Iris.warn("Chunk replacement waited " + acquireWait + "ms for load lock at " + x + "," + z + "."); } - IrisBiomeStorage st = new IrisBiomeStorage(); - TerrainChunk tc = TerrainChunk.createUnsafe(world, st); + TerrainChunk tc = TerrainChunk.create(world); this.world.bind(world); phase = "engine-generate"; @@ -481,16 +480,15 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun return; } computeStudioGenerator(); - TerrainChunk tc = TerrainChunk.create(d, new IrisBiomeStorage()); + TerrainChunk tc = TerrainChunk.create(d); this.world.bind(world); if (studioGenerator != null) { studioGenerator.generateChunk(engine, tc, x, z); } else { - ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(tc); - BiomeGridHunkHolder biomes = new BiomeGridHunkHolder(tc, tc.getMinHeight(), tc.getMaxHeight()); + ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(d); + Hunk biomes = Hunk.viewBiomes(tc); engine.generate(x << 4, z << 4, blocks, biomes, IrisSettings.get().getGenerator().useMulticore); blocks.apply(); - biomes.apply(); } Iris.debug("Generated " + x + " " + z); @@ -526,11 +524,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun return populators; } - @Override - public boolean isParallelCapable() { - return true; - } - @Override public boolean shouldGenerateCaves() { return false; @@ -562,8 +555,8 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun } @Override - public boolean shouldGenerateBedrock() { - return false; + public void generateBedrock(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull ChunkData chunkData) { + } @Nullable diff --git a/core/src/main/java/art/arcane/iris/engine/platform/DummyBiomeGrid.java b/core/src/main/java/art/arcane/iris/engine/platform/DummyBiomeGrid.java deleted file mode 100644 index f49a3ce03..000000000 --- a/core/src/main/java/art/arcane/iris/engine/platform/DummyBiomeGrid.java +++ /dev/null @@ -1,29 +0,0 @@ -package art.arcane.iris.engine.platform; - -import org.bukkit.block.Biome; -import org.bukkit.generator.ChunkGenerator; -import org.jetbrains.annotations.NotNull; - -public class DummyBiomeGrid implements ChunkGenerator.BiomeGrid { - @NotNull - @Override - public Biome getBiome(int x, int z) { - return null; - } - - @NotNull - @Override - public Biome getBiome(int x, int y, int z) { - return null; - } - - @Override - public void setBiome(int x, int z, @NotNull Biome bio) { - - } - - @Override - public void setBiome(int x, int y, int z, @NotNull Biome bio) { - - } -} diff --git a/core/src/main/java/art/arcane/iris/util/common/data/B.java b/core/src/main/java/art/arcane/iris/util/common/data/B.java index 839afd531..837834594 100644 --- a/core/src/main/java/art/arcane/iris/util/common/data/B.java +++ b/core/src/main/java/art/arcane/iris/util/common/data/B.java @@ -11,11 +11,14 @@ import art.arcane.iris.core.nms.INMS; import art.arcane.iris.core.nms.container.BlockProperty; import art.arcane.iris.core.service.ExternalDataSVC; import art.arcane.iris.util.common.data.registry.Materials; +import art.arcane.iris.util.common.reflect.KeyedType; import it.unimi.dsi.fastutil.ints.IntSet; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.block.data.BlockData; import java.util.List; +import java.util.Locale; import java.util.Optional; public class B { @@ -103,7 +106,9 @@ public class B { protected KMap, List> loadExternalBlockStates() { KMap, List> flipped = new KMap<>(); INMS.get().getBlockProperties().forEach((k, v) -> { - flipped.computeIfAbsent(v, $ -> new KList<>()).add(k.getKey().toString()); + NamespacedKey key = KeyedType.getKey(k); + String serialized = key == null ? k.name().toLowerCase(Locale.ROOT) : key.toString(); + flipped.computeIfAbsent(v, $ -> new KList<>()).add(serialized); }); var emptyStates = flipped.computeIfAbsent(new KList<>(0), $ -> new KList<>()); diff --git a/core/src/main/java/art/arcane/iris/util/common/format/C.java b/core/src/main/java/art/arcane/iris/util/common/format/C.java index dba2f0a71..e9cab0b24 100644 --- a/core/src/main/java/art/arcane/iris/util/common/format/C.java +++ b/core/src/main/java/art/arcane/iris/util/common/format/C.java @@ -443,7 +443,7 @@ public enum C { } public static String compress(String c) { - return BaseComponent.toLegacyText(TextComponent.fromLegacyText(c)); + return BaseComponent.toLegacyText(TextComponent.fromLegacy(c)); } /** @@ -768,4 +768,4 @@ public enum C { default -> (byte) 15; }; } -} \ No newline at end of file +} diff --git a/core/src/main/java/art/arcane/iris/util/common/nbt/mca/NBTWorld.java b/core/src/main/java/art/arcane/iris/util/common/nbt/mca/NBTWorld.java index 9b26c9942..732e4286c 100644 --- a/core/src/main/java/art/arcane/iris/util/common/nbt/mca/NBTWorld.java +++ b/core/src/main/java/art/arcane/iris/util/common/nbt/mca/NBTWorld.java @@ -26,15 +26,18 @@ import art.arcane.volmlib.util.nbt.mca.MCAWorldStoreSupport; import art.arcane.volmlib.util.nbt.mca.MCAWorldRuntimeSupport; import art.arcane.volmlib.util.nbt.mca.NBTWorldSupport; import art.arcane.iris.util.common.data.B; +import art.arcane.iris.util.common.reflect.KeyedType; import art.arcane.volmlib.util.math.M; import art.arcane.volmlib.util.nbt.tag.CompoundTag; import art.arcane.iris.util.common.parallel.HyperLock; import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; import java.io.File; import java.util.Map; +import java.util.Locale; public class NBTWorld { private static final BlockData AIR = B.get("AIR"); @@ -43,7 +46,10 @@ public class NBTWorld { B::getAir, blockData -> blockData.getAsString(true), blockData -> { - NamespacedKey key = blockData.getMaterial().getKey(); + NamespacedKey key = KeyedType.getKey(blockData.getMaterial()); + if (key == null) { + return "minecraft:" + blockData.getMaterial().name().toLowerCase(Locale.ROOT); + } return key.getNamespace() + ":" + key.getKey(); } ); @@ -127,8 +133,9 @@ public class NBTWorld { private static Map computeBiomeIDs() { Map biomeIds = new KMap<>(); - for (Biome biome : Biome.values()) { - if (!biome.name().equals("CUSTOM")) { + for (Biome biome : Registry.BIOME) { + NamespacedKey key = biome.getKeyOrNull(); + if (key != null && !key.getKey().equals("custom")) { biomeIds.put(biome, INMS.get().getBiomeId(biome)); } } diff --git a/core/src/main/java/art/arcane/iris/util/common/reflect/KeyedType.java b/core/src/main/java/art/arcane/iris/util/common/reflect/KeyedType.java index 5995e1d5c..53400adc1 100644 --- a/core/src/main/java/art/arcane/iris/util/common/reflect/KeyedType.java +++ b/core/src/main/java/art/arcane/iris/util/common/reflect/KeyedType.java @@ -6,14 +6,16 @@ import com.google.gson.reflect.TypeToken; import art.arcane.iris.util.common.data.registry.RegistryTypeAdapter; import art.arcane.iris.util.common.data.registry.RegistryUtil; import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; public class KeyedType { - private static final boolean KEYED_ENABLED = Boolean.getBoolean("iris.keyed-types"); private static final boolean KEYED_LENIENT = Boolean.getBoolean("iris.keyed-lenient"); public static String[] values(Class type) { if (!isKeyed(type)) return new String[0]; - if (!KEYED_ENABLED) return OldEnum.values(type); return RegistryUtil.lookup(type) .map() .keySet() @@ -23,15 +25,48 @@ public class KeyedType { } public static boolean isKeyed(Class type) { - if (KEYED_ENABLED) { - if (KEYED_LENIENT) return !RegistryUtil.lookup(type).isEmpty(); - else return Keyed.class.isAssignableFrom(type); - } else return OldEnum.isOldEnum(type); + if (KEYED_LENIENT) return !RegistryUtil.lookup(type).isEmpty(); + return Keyed.class.isAssignableFrom(type); } @SuppressWarnings("unchecked") public static TypeAdapter createTypeAdapter(Gson gson, TypeToken type) { if (!isKeyed(type.getRawType())) return null; - return (TypeAdapter) (KEYED_ENABLED ? RegistryTypeAdapter.of(type.getRawType()) : OldEnum.create(type.getRawType())); + return (TypeAdapter) RegistryTypeAdapter.of(type.getRawType()); + } + + @Nullable + public static NamespacedKey getKey(Object value) { + if (value == null) { + return null; + } + + if (value instanceof Keyed keyed) { + NamespacedKey key = keyed.getKey(); + if (key != null) { + return key; + } + } + + NamespacedKey keyOrThrow = invokeKey(value, "getKeyOrThrow"); + if (keyOrThrow != null) { + return keyOrThrow; + } + + return invokeKey(value, "getKey"); + } + + @Nullable + private static NamespacedKey invokeKey(Object value, String methodName) { + try { + Method method = value.getClass().getMethod(methodName); + Object result = method.invoke(value); + if (result instanceof NamespacedKey namespacedKey) { + return namespacedKey; + } + return null; + } catch (Throwable ignored) { + return null; + } } } diff --git a/core/src/main/java/art/arcane/iris/util/common/scheduling/jobs/DownloadJob.java b/core/src/main/java/art/arcane/iris/util/common/scheduling/jobs/DownloadJob.java index eed276a31..0dfc65e46 100644 --- a/core/src/main/java/art/arcane/iris/util/common/scheduling/jobs/DownloadJob.java +++ b/core/src/main/java/art/arcane/iris/util/common/scheduling/jobs/DownloadJob.java @@ -24,7 +24,7 @@ import art.arcane.volmlib.util.network.DownloadMonitor; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; -import java.net.URL; +import java.net.URI; public class DownloadJob implements Job { private final DL.Download download; @@ -34,7 +34,7 @@ public class DownloadJob implements Job { public DownloadJob(String url, File destination) throws MalformedURLException { tw = 1; cw = 0; - download = new DL.Download(new URL(url), destination, DL.DownloadFlag.CALCULATE_SIZE); + download = new DL.Download(URI.create(url).toURL(), destination, DL.DownloadFlag.CALCULATE_SIZE); download.monitor(new DownloadMonitor() { @Override public void onUpdate(DL.DownloadState state, double progress, long elapsed, long estimated, long bps, long iobps, long size, long downloaded, long buffer, double bufferuse) { diff --git a/core/src/main/java/art/arcane/iris/util/project/hunk/Hunk.java b/core/src/main/java/art/arcane/iris/util/project/hunk/Hunk.java index 9c68497d4..e7448c849 100644 --- a/core/src/main/java/art/arcane/iris/util/project/hunk/Hunk.java +++ b/core/src/main/java/art/arcane/iris/util/project/hunk/Hunk.java @@ -19,6 +19,7 @@ package art.arcane.iris.util.project.hunk; import art.arcane.iris.Iris; +import art.arcane.iris.engine.data.chunk.TerrainChunk; import art.arcane.iris.engine.object.IrisPosition; import art.arcane.volmlib.util.function.*; import art.arcane.volmlib.util.hunk.HunkComputeSupport; @@ -38,7 +39,6 @@ import art.arcane.iris.util.project.stream.interpolation.Interpolated; import org.bukkit.Chunk; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; -import org.bukkit.generator.ChunkGenerator.BiomeGrid; import org.bukkit.generator.ChunkGenerator.ChunkData; import java.io.IOException; @@ -94,14 +94,18 @@ public interface Hunk extends HunkLike { return adapt(new art.arcane.volmlib.util.hunk.view.FunctionalHunkView<>(unwrap(src), reader, writer)); } - static Hunk view(BiomeGrid biome, int minHeight, int maxHeight) { - return new BiomeGridHunkView(biome, minHeight, maxHeight); - } - static Hunk fringe(Hunk i, Hunk o) { return adapt(new art.arcane.volmlib.util.hunk.view.FringedHunkView<>(unwrap(i), unwrap(o))); } + static Hunk view(TerrainChunk src) { + return new ChunkDataHunkView(src.getChunkData()); + } + + static Hunk viewBiomes(TerrainChunk src) { + return new TerrainChunkBiomeHunkView(src); + } + static Hunk view(ChunkData src) { return new ChunkDataHunkView(src); } diff --git a/core/src/main/java/art/arcane/iris/util/project/hunk/view/BiomeGridHunkHolder.java b/core/src/main/java/art/arcane/iris/util/project/hunk/view/BiomeGridHunkHolder.java deleted file mode 100644 index 3c13b24f4..000000000 --- a/core/src/main/java/art/arcane/iris/util/project/hunk/view/BiomeGridHunkHolder.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2022 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.util.project.hunk.view; - -import art.arcane.iris.core.nms.INMS; -import art.arcane.iris.engine.data.chunk.LinkedTerrainChunk; -import art.arcane.iris.util.project.hunk.Hunk; -import art.arcane.volmlib.util.hunk.view.BiomeGridForceSupport; -import org.bukkit.block.Biome; -import org.bukkit.generator.ChunkGenerator.BiomeGrid; - -@SuppressWarnings("ClassCanBeRecord") -public class BiomeGridHunkHolder extends art.arcane.volmlib.util.hunk.view.BiomeGridHunkHolder implements Hunk { - public BiomeGridHunkHolder(BiomeGrid chunk, int minHeight, int maxHeight) { - super(chunk, minHeight, maxHeight); - } - - public void forceBiomeBaseInto(int x, int y, int z, Object somethingVeryDirty) { - BiomeGridForceSupport.forceBiomeBaseInto( - getChunk(), - getMinHeight(), - x, - y, - z, - somethingVeryDirty, - chunk -> chunk instanceof LinkedTerrainChunk ? ((LinkedTerrainChunk) chunk).getRawBiome() : chunk, - (wx, wy, wz, dirty, target) -> INMS.get().forceBiomeInto(wx, wy, wz, dirty, target)); - } -} diff --git a/core/src/main/java/art/arcane/iris/util/project/hunk/view/BiomeGridHunkView.java b/core/src/main/java/art/arcane/iris/util/project/hunk/view/BiomeGridHunkView.java deleted file mode 100644 index c75dba7ba..000000000 --- a/core/src/main/java/art/arcane/iris/util/project/hunk/view/BiomeGridHunkView.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2022 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.util.project.hunk.view; - -import art.arcane.iris.core.nms.INMS; -import art.arcane.iris.engine.data.chunk.LinkedTerrainChunk; -import art.arcane.iris.util.project.hunk.Hunk; -import art.arcane.volmlib.util.hunk.view.BiomeGridForceSupport; -import org.bukkit.block.Biome; -import org.bukkit.generator.ChunkGenerator.BiomeGrid; - -@SuppressWarnings("ClassCanBeRecord") -public class BiomeGridHunkView extends art.arcane.volmlib.util.hunk.view.BiomeGridHunkView implements Hunk { - public BiomeGridHunkView(BiomeGrid chunk, int minHeight, int maxHeight) { - super(chunk, minHeight, maxHeight); - } - - public void forceBiomeBaseInto(int x, int y, int z, Object somethingVeryDirty) { - BiomeGridForceSupport.forceBiomeBaseInto( - getChunk(), - getMinHeight(), - x, - y, - z, - somethingVeryDirty, - chunk -> chunk instanceof LinkedTerrainChunk ? ((LinkedTerrainChunk) chunk).getRawBiome() : chunk, - (wx, wy, wz, dirty, target) -> INMS.get().forceBiomeInto(wx, wy, wz, dirty, target)); - } -} diff --git a/core/src/main/java/art/arcane/iris/util/project/hunk/view/TerrainChunkBiomeHunkView.java b/core/src/main/java/art/arcane/iris/util/project/hunk/view/TerrainChunkBiomeHunkView.java new file mode 100644 index 000000000..9ad5a6ed6 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/util/project/hunk/view/TerrainChunkBiomeHunkView.java @@ -0,0 +1,43 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package art.arcane.iris.util.project.hunk.view; + +import art.arcane.iris.engine.data.chunk.TerrainChunk; +import art.arcane.iris.util.project.hunk.Hunk; +import art.arcane.iris.util.project.hunk.storage.StorageHunk; +import org.bukkit.block.Biome; + +public class TerrainChunkBiomeHunkView extends StorageHunk implements Hunk { + private final TerrainChunk chunk; + + public TerrainChunkBiomeHunkView(TerrainChunk chunk) { + super(16, chunk.getMaxHeight() - chunk.getMinHeight(), 16); + this.chunk = chunk; + } + + @Override + public void setRaw(int x, int y, int z, Biome biome) { + chunk.setBiome(x, y + chunk.getMinHeight(), z, biome); + } + + @Override + public Biome getRaw(int x, int y, int z) { + return chunk.getBiome(x, y + chunk.getMinHeight(), z); + } +} diff --git a/core/src/main/kotlin/art/arcane/iris/core/safeguard/Mode.kt b/core/src/main/kotlin/art/arcane/iris/core/safeguard/Mode.kt index fd6afa6eb..6c3bd6604 100644 --- a/core/src/main/kotlin/art/arcane/iris/core/safeguard/Mode.kt +++ b/core/src/main/kotlin/art/arcane/iris/core/safeguard/Mode.kt @@ -61,7 +61,7 @@ enum class Mode(private val color: C) { "", padd2 + color + " Iris, " + C.AQUA + "Iris, Dimension Engine " + C.RED + "[" + releaseTrain + " RELEASE]", padd2 + C.GRAY + " Version: " + color + version, - padd2 + C.GRAY + " By: " + color + "Arcane Arts (Volmit Software)", + padd2 + C.GRAY + " By: " + color + "Volmit Software (Arcane Arts)", padd2 + C.GRAY + " Server: " + color + serverVersion, padd2 + C.GRAY + " Java: " + color + javaVersion + C.GRAY + " | Date: " + color + startupDate, padd2 + C.GRAY + " Commit: " + color + BuildConstants.COMMIT + C.GRAY + "/" + color + BuildConstants.ENVIRONMENT, diff --git a/nms/v1_21_R7/src/main/java/art/arcane/iris/core/nms/v1_21_R7/CustomBiomeSource.java b/nms/v1_21_R7/src/main/java/art/arcane/iris/core/nms/v1_21_R7/CustomBiomeSource.java index c250ad98d..8dd96e0cd 100644 --- a/nms/v1_21_R7/src/main/java/art/arcane/iris/core/nms/v1_21_R7/CustomBiomeSource.java +++ b/nms/v1_21_R7/src/main/java/art/arcane/iris/core/nms/v1_21_R7/CustomBiomeSource.java @@ -25,6 +25,7 @@ import org.bukkit.craftbukkit.v1_21_R7.CraftWorld; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; import java.util.stream.Stream; @@ -38,6 +39,7 @@ public class CustomBiomeSource extends BiomeSource { private final AtomicCache registryAccess = new AtomicCache<>(); private final RNG rng; private final KMap> customBiomes; + private final Holder fallbackBiome; public CustomBiomeSource(long seed, Engine engine, World world) { this.engine = engine; @@ -45,24 +47,37 @@ public class CustomBiomeSource extends BiomeSource { this.biomeCustomRegistry = registry().lookup(Registries.BIOME).orElse(null); this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).lookup(Registries.BIOME).orElse(null); this.rng = new RNG(engine.getSeedManager().getBiome()); - this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine); + this.fallbackBiome = resolveFallbackBiome(this.biomeRegistry, this.biomeCustomRegistry); + this.customBiomes = fillCustomBiomes(this.biomeCustomRegistry, engine, this.fallbackBiome); } - private static List> getAllBiomes(Registry customRegistry, Registry registry, Engine engine) { - List> b = new ArrayList<>(); + private static List> getAllBiomes(Registry customRegistry, Registry registry, Engine engine, Holder fallback) { + LinkedHashSet> biomes = new LinkedHashSet<>(); + if (fallback != null) { + biomes.add(fallback); + } for (IrisBiome i : engine.getAllBiomes()) { if (i.isCustom()) { for (IrisBiomeCustom j : i.getCustomDerivitives()) { - b.add(customRegistry.get(customRegistry.getResourceKey(customRegistry - .getValue(Identifier.fromNamespaceAndPath(engine.getDimension().getLoadKey().toLowerCase(java.util.Locale.ROOT), j.getId().toLowerCase(java.util.Locale.ROOT)))).get()).get()); + Holder customHolder = resolveCustomBiomeHolder(customRegistry, engine, j.getId()); + if (customHolder != null) { + biomes.add(customHolder); + } else if (fallback != null) { + biomes.add(fallback); + } } } else { - b.add(NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative())); + Holder vanillaHolder = NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative()); + if (vanillaHolder != null) { + biomes.add(vanillaHolder); + } else if (fallback != null) { + biomes.add(fallback); + } } } - return b; + return new ArrayList<>(biomes); } private static Object getFor(Class type, Object source) { @@ -117,28 +132,28 @@ public class CustomBiomeSource extends BiomeSource { ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())) .lookup(Registries.BIOME).orElse(null), ((CraftWorld) engine.getWorld().realWorld()).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null), - engine).stream(); + engine, + fallbackBiome).stream(); } - private KMap> fillCustomBiomes(Registry customRegistry, Engine engine) { + + private KMap> fillCustomBiomes(Registry customRegistry, Engine engine, Holder fallback) { KMap> m = new KMap<>(); + if (customRegistry == null) { + return m; + } for (IrisBiome i : engine.getAllBiomes()) { if (i.isCustom()) { for (IrisBiomeCustom j : i.getCustomDerivitives()) { - Identifier resourceLocation = Identifier.fromNamespaceAndPath(engine.getDimension().getLoadKey().toLowerCase(java.util.Locale.ROOT), j.getId().toLowerCase(java.util.Locale.ROOT)); - Biome biome = customRegistry.getValue(resourceLocation); - Optional> optionalBiomeKey = customRegistry.getResourceKey(biome); - if (optionalBiomeKey.isEmpty()) { + Holder holder = resolveCustomBiomeHolder(customRegistry, engine, j.getId()); + if (holder == null) { + if (fallback != null) { + m.put(j.getId(), fallback); + } Iris.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName()); continue; } - ResourceKey biomeKey = optionalBiomeKey.get(); - Optional> optionalReferenceHolder = customRegistry.get(biomeKey); - if (optionalReferenceHolder.isEmpty()) { - Iris.error("Cannot find reference to biome " + biomeKey + " for engine " + engine.getName()); - continue; - } - m.put(j.getId(), optionalReferenceHolder.get()); + m.put(j.getId(), holder); } } } @@ -159,11 +174,89 @@ public class CustomBiomeSource extends BiomeSource { public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { int m = (y - engine.getMinHeight()) << 2; IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2); - if (ib.isCustom()) { - return customBiomes.get(ib.getCustomBiome(rng, x << 2, m, z << 2).getId()); - } else { - org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2); - return NMSBinding.biomeToBiomeBase(biomeRegistry, v); + if (ib == null) { + return resolveFallbackBiome(biomeRegistry, biomeCustomRegistry); } + + if (ib.isCustom()) { + IrisBiomeCustom custom = ib.getCustomBiome(rng, x << 2, m, z << 2); + if (custom != null) { + Holder holder = customBiomes.get(custom.getId()); + if (holder != null) { + return holder; + } + } + + return resolveFallbackBiome(biomeRegistry, biomeCustomRegistry); + } + + org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2); + Holder holder = NMSBinding.biomeToBiomeBase(biomeRegistry, v); + if (holder != null) { + return holder; + } + + return resolveFallbackBiome(biomeRegistry, biomeCustomRegistry); } -} \ No newline at end of file + + private static Holder resolveCustomBiomeHolder(Registry customRegistry, Engine engine, String customBiomeId) { + if (customRegistry == null || engine == null || customBiomeId == null || customBiomeId.isBlank()) { + return null; + } + + Identifier resourceLocation = Identifier.fromNamespaceAndPath( + engine.getDimension().getLoadKey().toLowerCase(java.util.Locale.ROOT), + customBiomeId.toLowerCase(java.util.Locale.ROOT) + ); + Biome biome = customRegistry.getValue(resourceLocation); + if (biome == null) { + return null; + } + + Optional> optionalBiomeKey = customRegistry.getResourceKey(biome); + if (optionalBiomeKey.isEmpty()) { + return null; + } + + Optional> optionalReferenceHolder = customRegistry.get(optionalBiomeKey.get()); + if (optionalReferenceHolder.isEmpty()) { + return null; + } + + return optionalReferenceHolder.get(); + } + + private static Holder resolveFallbackBiome(Registry registry, Registry customRegistry) { + Holder plains = NMSBinding.biomeToBiomeBase(registry, org.bukkit.block.Biome.PLAINS); + if (plains != null) { + return plains; + } + + Holder vanilla = firstHolder(registry); + if (vanilla != null) { + return vanilla; + } + + return firstHolder(customRegistry); + } + + private static Holder firstHolder(Registry registry) { + if (registry == null) { + return null; + } + + for (Biome biome : registry) { + Optional> optionalBiomeKey = registry.getResourceKey(biome); + if (optionalBiomeKey.isEmpty()) { + continue; + } + + Optional> optionalHolder = registry.get(optionalBiomeKey.get()); + if (optionalHolder.isPresent()) { + return optionalHolder.get(); + } + } + + return null; + } +} diff --git a/nms/v1_21_R7/src/main/java/art/arcane/iris/core/nms/v1_21_R7/IrisChunkGenerator.java b/nms/v1_21_R7/src/main/java/art/arcane/iris/core/nms/v1_21_R7/IrisChunkGenerator.java index 098c9611e..d4081314a 100644 --- a/nms/v1_21_R7/src/main/java/art/arcane/iris/core/nms/v1_21_R7/IrisChunkGenerator.java +++ b/nms/v1_21_R7/src/main/java/art/arcane/iris/core/nms/v1_21_R7/IrisChunkGenerator.java @@ -402,12 +402,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator { delegate.validate(); } - @Override - @SuppressWarnings("deprecation") - public BiomeGenerationSettings getBiomeGenerationSettings(Holder holder) { - return delegate.getBiomeGenerationSettings(holder); - } - static { Field biomeSource = null; for (Field field : ChunkGenerator.class.getDeclaredFields()) { diff --git a/nms/v1_21_R7/src/main/java/art/arcane/iris/core/nms/v1_21_R7/NMSBinding.java b/nms/v1_21_R7/src/main/java/art/arcane/iris/core/nms/v1_21_R7/NMSBinding.java index 547013f58..5f6d4428d 100644 --- a/nms/v1_21_R7/src/main/java/art/arcane/iris/core/nms/v1_21_R7/NMSBinding.java +++ b/nms/v1_21_R7/src/main/java/art/arcane/iris/core/nms/v1_21_R7/NMSBinding.java @@ -108,7 +108,6 @@ public class NMSBinding implements INMSBinding { private final AtomicCache> globalCache = new AtomicCache<>(); private final AtomicCache registryAccess = new AtomicCache<>(); private final AtomicCache byIdRef = new AtomicCache<>(); - private Field biomeStorageCache = null; private static Object getFor(Class type, Object source) { Object o = fieldFor(type, source); @@ -390,7 +389,11 @@ public class NMSBinding implements INMSBinding { @Override public KList getBiomes() { - return new KList<>(Biome.values()).qadd(Biome.CHERRY_GROVE).qdel(Biome.CUSTOM); + KList biomes = new KList<>(); + for (Biome biome : org.bukkit.Registry.BIOME) { + biomes.add(biome); + } + return biomes; } @Override @@ -407,7 +410,12 @@ public class NMSBinding implements INMSBinding { } } - return biome.ordinal(); + List biomes = new ArrayList<>(); + for (Biome entry : org.bukkit.Registry.BIOME) { + biomes.add(entry); + } + int index = biomes.indexOf(biome); + return Math.max(index, 0); } private MCAIdMap getBiomeMapping() { @@ -488,38 +496,6 @@ public class NMSBinding implements INMSBinding { c.markUnsaved(); } - @Override - public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) { - try { - ChunkAccess s = (ChunkAccess) getFieldForBiomeStorage(chunk).get(chunk); - Holder biome = (Holder) somethingVeryDirty; - s.setBiome(x, y, z, biome); - } catch (IllegalAccessException e) { - Iris.reportError(e); - e.printStackTrace(); - } - } - - private Field getFieldForBiomeStorage(Object storage) { - Field f = biomeStorageCache; - - if (f != null) { - return f; - } - try { - f = storage.getClass().getDeclaredField("biome"); - f.setAccessible(true); - return f; - } catch (Throwable e) { - Iris.reportError(e); - e.printStackTrace(); - Iris.error(storage.getClass().getCanonicalName()); - } - - biomeStorageCache = f; - return null; - } - @Override public MCAPaletteAccess createPalette() { MCAIdMapper registry = registryCache.aquireNasty(() -> { @@ -598,31 +574,41 @@ public class NMSBinding implements INMSBinding { } public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { - Field[] fields = EntityType.class.getDeclaredFields(); - for (Field field : fields) { - if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) { - try { - EntityType entityType = (EntityType) field.get(null); - if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) { - Vector v1 = new Vector<>(); - v1.add(entityType.getHeight()); - entityType.getDimensions(); - Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth()); - //System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width); - return box; - } - } catch (IllegalAccessException e) { - Iris.error("Unable to get entity dimensions!"); - e.printStackTrace(); + if (entity == null) { + return null; + } + + try { + String descriptionId = "entity.minecraft." + entity.name().toLowerCase(Locale.ROOT); + Field[] fields = EntityType.class.getDeclaredFields(); + for (Field field : fields) { + if (!Modifier.isStatic(field.getModifiers()) || !field.getType().equals(EntityType.class)) { + continue; + } + + EntityType entityType = (EntityType) field.get(null); + if (entityType == null) { + continue; + } + + if (descriptionId.equals(entityType.getDescriptionId())) { + return new Vector3d(entityType.getWidth(), entityType.getHeight(), entityType.getWidth()); } } + return null; + } catch (Throwable e) { + Iris.error("Unable to get entity dimensions for " + entity + "!"); + Iris.reportError(e); + return null; } - return null; } @Override public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) { + if (location == null || location.getWorld() == null || type == null || type.getEntityClass() == null) { + return null; + } return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason); } @@ -670,7 +656,49 @@ public class NMSBinding implements INMSBinding { } public static Holder biomeToBiomeBase(Registry registry, Biome biome) { - return registry.getOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey()))); + if (registry == null || biome == null) { + return null; + } + + NamespacedKey biomeKey = resolveBiomeKey(biome); + if (biomeKey == null) { + return null; + } + + ResourceKey key = ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biomeKey)); + return registry.get(key).orElse(null); + } + + private static NamespacedKey resolveBiomeKey(Biome biome) { + Object keyOrNullValue = invokeNoThrow(biome, "getKeyOrNull", new Class[0]); + if (keyOrNullValue instanceof NamespacedKey namespacedKey) { + return namespacedKey; + } + + Object keyOrThrowValue = invokeNoThrow(biome, "getKeyOrThrow", new Class[0]); + if (keyOrThrowValue instanceof NamespacedKey namespacedKey) { + return namespacedKey; + } + + Object keyValue = invokeNoThrow(biome, "getKey", new Class[0]); + if (keyValue instanceof NamespacedKey namespacedKey) { + return namespacedKey; + } + + return null; + } + + private static Object invokeNoThrow(Object target, String methodName, Class[] parameterTypes, Object... args) { + if (target == null) { + return null; + } + + try { + Method method = target.getClass().getMethod(methodName, parameterTypes); + return method.invoke(target, args); + } catch (Throwable ignored) { + return null; + } } @Override diff --git a/settings.gradle.kts b/settings.gradle.kts index e7a4551f1..c432e4d2b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,13 +23,41 @@ plugins { rootProject.name = "Iris" +fun hasVolmLibSettings(directory: File): Boolean { + return directory.resolve("settings.gradle.kts").exists() || directory.resolve("settings.gradle").exists() +} + +fun resolveLocalVolmLibDirectory(): File? { + val configuredPath: String? = providers.gradleProperty("localVolmLibDirectory") + .orElse(providers.environmentVariable("VOLMLIB_DIR")) + .orNull + if (!configuredPath.isNullOrBlank()) { + val configuredDirectory: File = file(configuredPath) + if (hasVolmLibSettings(configuredDirectory)) { + return configuredDirectory + } + } + + var currentDirectory: File? = settingsDir + while (currentDirectory != null) { + val candidate: File = currentDirectory.resolve("VolmLib") + if (hasVolmLibSettings(candidate)) { + return candidate + } + + currentDirectory = currentDirectory.parentFile + } + + return null +} + val useLocalVolmLib: Boolean = providers.gradleProperty("useLocalVolmLib") .orElse("true") .map { value: String -> value.equals("true", ignoreCase = true) } .get() -val localVolmLibDirectory: File = file("../VolmLib") +val localVolmLibDirectory: File? = resolveLocalVolmLibDirectory() -if (useLocalVolmLib && localVolmLibDirectory.resolve("settings.gradle.kts").exists()) { +if (useLocalVolmLib && localVolmLibDirectory != null) { includeBuild(localVolmLibDirectory) { dependencySubstitution { substitute(module("com.github.VolmitSoftware:VolmLib")).using(project(":shared"))