From 488b76d1d20f2248e1a0972f7da667402dcdb021 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 2 Aug 2024 18:14:41 +0200 Subject: [PATCH] add dummy datapack + fix world creation --- core/src/main/java/com/volmit/iris/Iris.java | 1 + .../volmit/iris/core/ServerConfigurator.java | 43 +++++++++ .../com/volmit/iris/core/nms/INMSBinding.java | 10 ++ .../core/nms/container/IPackRepository.java | 14 +++ .../iris/core/nms/v1X/NMSBinding1X.java | 11 +++ .../iris/core/nms/v1X/PackRepository1X.java | 43 +++++++++ .../volmit/iris/core/tools/IrisCreator.java | 5 +- .../engine/platform/BukkitChunkGenerator.java | 3 + .../platform/PlatformChunkGenerator.java | 3 + .../core/nms/v1_19_R1/CustomBiomeSource.java | 2 + .../iris/core/nms/v1_19_R1/NMSBinding.java | 83 ++++++++++++++++- .../core/nms/v1_19_R1/WPackRepository.java | 91 ++++++++++++++++++ .../core/nms/v1_19_R2/CustomBiomeSource.java | 2 + .../iris/core/nms/v1_19_R2/NMSBinding.java | 84 ++++++++++++++++- .../core/nms/v1_19_R2/WPackRepository.java | 93 +++++++++++++++++++ .../core/nms/v1_19_R3/CustomBiomeSource.java | 2 + .../iris/core/nms/v1_19_R3/NMSBinding.java | 84 ++++++++++++++++- .../core/nms/v1_19_R3/WPackRepository.java | 76 +++++++++++++++ .../core/nms/v1_20_R1/CustomBiomeSource.java | 2 + .../iris/core/nms/v1_20_R1/NMSBinding.java | 84 ++++++++++++++++- .../core/nms/v1_20_R1/WPackRepository.java | 76 +++++++++++++++ .../core/nms/v1_20_R2/CustomBiomeSource.java | 2 + .../iris/core/nms/v1_20_R2/NMSBinding.java | 86 ++++++++++++++++- .../core/nms/v1_20_R2/WPackRepository.java | 76 +++++++++++++++ .../core/nms/v1_20_R3/CustomBiomeSource.java | 2 + .../iris/core/nms/v1_20_R3/Headless.java | 2 + .../iris/core/nms/v1_20_R3/NMSBinding.java | 84 ++++++++++++++++- .../core/nms/v1_20_R3/WPackRepository.java | 76 +++++++++++++++ .../core/nms/v1_20_R4/CustomBiomeSource.java | 2 + .../iris/core/nms/v1_20_R4/NMSBinding.java | 92 +++++++++++++++++- .../core/nms/v1_20_R4/WPackRepository.java | 76 +++++++++++++++ .../core/nms/v1_21_R1/CustomBiomeSource.java | 15 ++- .../iris/core/nms/v1_21_R1/NMSBinding.java | 87 +++++++++++++++++ .../core/nms/v1_21_R1/WPackRepository.java | 76 +++++++++++++++ 34 files changed, 1450 insertions(+), 38 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/core/nms/container/IPackRepository.java create mode 100644 core/src/main/java/com/volmit/iris/core/nms/v1X/PackRepository1X.java create mode 100644 nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/WPackRepository.java create mode 100644 nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/WPackRepository.java create mode 100644 nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/WPackRepository.java create mode 100644 nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/WPackRepository.java create mode 100644 nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/WPackRepository.java create mode 100644 nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/WPackRepository.java create mode 100644 nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/WPackRepository.java create mode 100644 nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/WPackRepository.java diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index deb402697..7b15ac9ac 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -469,6 +469,7 @@ public class Iris extends VolmitPlugin implements Listener { IrisSafeguard.instance.IrisSafeguardSystem(); getSender().setTag(getTag()); INMS.get().injectBukkit(); + ServerConfigurator.disableDataPack(); if (IrisSafeguard.instance.unstablemode && !IrisSafeguard.instance.acceptUnstable) IrisSafeguard.instance.earlySplash(); compat = IrisCompat.configured(getDataFile("compat.json")); linkMultiverseCore = new MultiverseCoreLink(); diff --git a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java index dc75b46df..d82637495 100644 --- a/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java +++ b/core/src/main/java/com/volmit/iris/core/ServerConfigurator.java @@ -19,13 +19,18 @@ package com.volmit.iris.core; import com.volmit.iris.Iris; +import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.scheduling.J; +import org.bukkit.Bukkit; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.util.Properties; import java.util.concurrent.TimeUnit; public class ServerConfigurator { @@ -67,4 +72,42 @@ public class ServerConfigurator { f.save(spigotConfig); } } + + private static KList getDataPacksFolder() { + if (!IrisSettings.get().getGeneral().forceMainWorld.isEmpty()) { + return new KList().qadd(new File(Bukkit.getWorldContainer(), IrisSettings.get().getGeneral().forceMainWorld + "/datapacks")); + } + KList worlds = new KList<>(); + Bukkit.getServer().getWorlds().forEach(w -> worlds.add(new File(w.getWorldFolder(), "datapacks"))); + if (worlds.isEmpty()) { + worlds.add(new File(getMainWorldFolder(), "datapacks")); + } + return worlds; + } + + private static File getMainWorldFolder() { + try { + Properties prop = new Properties(); + prop.load(new FileInputStream("server.properties")); + String world = prop.getProperty("level-name", "world"); + return new File(Bukkit.getWorldContainer(), world); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static void dumpDataPack() { + if (!INMS.get().dumpRegistry(getDataPacksFolder().toArray(File[]::new))) + return; + disableDataPack(); + } + + public static void disableDataPack() { + var packs = INMS.get().getPackRepository(); + packs.reload(); + if (!packs.removePack("file/iris")) + return; + packs.reloadWorldData(); + } } diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java index 0ad25e7b2..c6866cccc 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java @@ -19,6 +19,7 @@ package com.volmit.iris.core.nms; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.IPackRepository; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.IrisBiomeCustom; @@ -43,6 +44,7 @@ import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; import java.awt.*; +import java.io.File; public interface INMSBinding { boolean hasTile(Location l); @@ -127,9 +129,17 @@ public interface INMSBinding { boolean registerBiome(String dimensionId, IrisBiomeCustom biome, boolean replace); + boolean dumpRegistry(File... folders); + void injectBukkit(); default IHeadless createHeadless(Engine engine) { throw new IllegalStateException("Headless mode not supported"); } + + default int getSpawnChunkCount(World world) { + return 441; + } + + IPackRepository getPackRepository(); } diff --git a/core/src/main/java/com/volmit/iris/core/nms/container/IPackRepository.java b/core/src/main/java/com/volmit/iris/core/nms/container/IPackRepository.java new file mode 100644 index 000000000..7585076cc --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/nms/container/IPackRepository.java @@ -0,0 +1,14 @@ +package com.volmit.iris.core.nms.container; + +import java.util.Collection; + +public interface IPackRepository { + void reload(); + void reloadWorldData(); + void setSelected(Collection packs); + boolean addPack(String packId); + boolean removePack(String packId); + Collection getAvailableIds(); + Collection getSelectedIds(); + boolean isAvailable(String packId); +} diff --git a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java index e52e80a44..aa549194a 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java +++ b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java @@ -23,6 +23,7 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BlockPos; +import com.volmit.iris.core.nms.container.IPackRepository; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.IrisBiomeCustom; import com.volmit.iris.engine.object.IrisDimension; @@ -121,6 +122,11 @@ public class NMSBinding1X implements INMSBinding { return false; } + @Override + public boolean dumpRegistry(File... folders) { + return false; + } + @Override public Color getBiomeColor(Location location, BiomeColor type) { return Color.GREEN; @@ -272,6 +278,11 @@ public class NMSBinding1X implements INMSBinding { } + @Override + public IPackRepository getPackRepository() { + return new PackRepository1X(); + } + private static class WorldCreatorAdvice { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) String name) { diff --git a/core/src/main/java/com/volmit/iris/core/nms/v1X/PackRepository1X.java b/core/src/main/java/com/volmit/iris/core/nms/v1X/PackRepository1X.java new file mode 100644 index 000000000..a3d72bbb4 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/nms/v1X/PackRepository1X.java @@ -0,0 +1,43 @@ +package com.volmit.iris.core.nms.v1X; + +import com.volmit.iris.core.nms.container.IPackRepository; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +class PackRepository1X implements IPackRepository { + @Override + public void reload() {} + + @Override + public void reloadWorldData() {} + + @Override + public void setSelected(Collection packs) {} + + @Override + public boolean addPack(String packId) { + return false; + } + + @Override + public boolean removePack(String packId) { + return false; + } + + @Override + public Collection getAvailableIds() { + return List.of(); + } + + @Override + public Collection getSelectedIds() { + return List.of(); + } + + @Override + public boolean isAvailable(String packId) { + return false; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java b/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java index 23a086c76..d24677d6b 100644 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java @@ -21,6 +21,7 @@ package com.volmit.iris.core.tools; import com.google.common.util.concurrent.AtomicDouble; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.pregenerator.PregenTask; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.engine.object.IrisDimension; @@ -147,7 +148,6 @@ public class IrisCreator { J.a(() -> { - int req = 441; Supplier g = () -> { if (finalAccess1 == null || finalAccess1.getEngine() == null) { return 0; @@ -155,6 +155,9 @@ public class IrisCreator { return finalAccess1.getEngine().getGenerated(); }; if(!benchmark) { + if (finalAccess1 == null) return; + int req = finalAccess1.getSpawnChunks().join(); + while (g.get() < req) { double v = (double) g.get() / (double) req; if (sender.isPlayer()) { diff --git a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index 35e6461c2..0b98077d7 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -65,6 +65,7 @@ import java.io.IOException; import java.lang.reflect.Field; import java.util.List; import java.util.Random; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -86,6 +87,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun private final AtomicBoolean setup; private final boolean studio; private final AtomicInteger a = new AtomicInteger(0); + private final CompletableFuture spawnChunks = new CompletableFuture<>(); private final boolean smartVanillaHeight; private Engine engine; private Looper hotloader; @@ -150,6 +152,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun } else { INMS.get().inject(event.getWorld().getSeed(), engine, event.getWorld()); Iris.info("Injected Iris Biome Source into " + event.getWorld().getName()); + spawnChunks.complete(INMS.get().getSpawnChunkCount(event.getWorld())); initialized = true; } } diff --git a/core/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java index fcedc1a3d..e79c3dd6f 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/PlatformChunkGenerator.java @@ -25,6 +25,7 @@ import com.volmit.iris.engine.framework.Hotloadable; import com.volmit.iris.util.data.DataProvider; import org.bukkit.World; +import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; public interface PlatformChunkGenerator extends Hotloadable, DataProvider { @@ -46,4 +47,6 @@ public interface PlatformChunkGenerator extends Hotloadable, DataProvider { boolean isStudio(); void touch(World world); + + CompletableFuture getSpawnChunks(); } diff --git a/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/CustomBiomeSource.java b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/CustomBiomeSource.java index 5a1f89970..3c7fed8dd 100644 --- a/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/CustomBiomeSource.java +++ b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/CustomBiomeSource.java @@ -2,6 +2,7 @@ package com.volmit.iris.core.nms.v1_19_R1; import com.mojang.serialization.Codec; import com.volmit.iris.Iris; +import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; @@ -146,6 +147,7 @@ public class CustomBiomeSource extends BiomeSource { } } } + ServerConfigurator.dumpDataPack(); return m; } diff --git a/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java index aaf718a2f..aa587e0cd 100644 --- a/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java +++ b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java @@ -1,11 +1,7 @@ package com.volmit.iris.core.nms.v1_19_R1; import java.awt.Color; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; +import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -17,8 +13,12 @@ import java.util.Vector; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; +import java.util.stream.Collectors; +import com.google.gson.JsonNull; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.IPackRepository; +import com.volmit.iris.util.io.IO; import net.minecraft.world.level.LevelReader; import com.google.common.base.Preconditions; import com.google.gson.JsonElement; @@ -101,6 +101,8 @@ import sun.misc.Unsafe; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); + private final WPackRepository packRepository = new WPackRepository(); + private final KMap, Boolean> changedRegistries = new KMap<>(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); @@ -623,6 +625,7 @@ public class NMSBinding implements INMSBinding { try { var holder = registry.register(key, value, Lifecycle.stable()); if (frozen) valueField.set(holder, value); + changedRegistries.put(registryKey, true); return true; } finally { field.setBoolean(registry, frozen); @@ -659,6 +662,7 @@ public class NMSBinding implements INMSBinding { toId.put(value, toId.removeInt(oldValue)); byValue.put(value, byValue.remove(oldValue)); lifecycles.put(value, lifecycles.remove(oldValue)); + changedRegistries.put(registryKey, true); return true; } catch (Throwable e) { throw new IllegalStateException(e); @@ -696,6 +700,70 @@ public class NMSBinding implements INMSBinding { } } + @Override + public boolean dumpRegistry(File... folders) { + var biomes = collect(Registry.BIOME_REGISTRY, net.minecraft.world.level.biome.Biome.DIRECT_CODEC); + var dimensions = collect(Registry.DIMENSION_TYPE_REGISTRY, DimensionType.DIRECT_CODEC); + + if (biomes.isEmpty() && dimensions.isEmpty()) + return false; + + for (File folder : folders) { + if (folder.getName().equals("datapacks")) + folder = new File(folder, "iris"); + File data = new File(folder, "data"); + + for (var entry : biomes.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/worldgen/biome/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write biome " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + for (var entry : dimensions.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/dimension_type/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write dimension " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + File meta = new File(folder, "pack.mcmeta"); + if (!meta.getParentFile().exists() && !meta.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(meta, "{\"pack\": {\"pack_format\": "+getDataVersion().getPackFormat()+", \"description\": \"Iris Data Pack. This pack contains all installed Iris Packs' resources.\"}}"); + } catch (IOException e) { + Iris.error("Failed to write pack.mcmeta to " + meta.getPath()); + } + } + + return true; + } + + private Map collect(ResourceKey> registryKey, Codec codec) { + var registry = registry().registry(registryKey).orElse(null); + if (registry == null || !changedRegistries.getOrDefault(registryKey, false)) + return Map.of(); + try { + return registry + .registryKeySet() + .stream() + .collect(Collectors.toMap(ResourceKey::location, id -> encode(codec, registry.get(id)).orElse(JsonNull.INSTANCE))); + } finally { + changedRegistries.put(registryKey, false); + } + } @Override public Color getBiomeColor(Location location, BiomeColor type) { @@ -762,6 +830,11 @@ public class NMSBinding implements INMSBinding { } + @Override + public IPackRepository getPackRepository() { + return packRepository; + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { diff --git a/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/WPackRepository.java b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/WPackRepository.java new file mode 100644 index 000000000..aed623dbc --- /dev/null +++ b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/WPackRepository.java @@ -0,0 +1,91 @@ +package com.volmit.iris.core.nms.v1_19_R1; + +import com.google.common.collect.ImmutableList; +import com.volmit.iris.core.nms.container.IPackRepository; +import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.world.level.DataPackConfig; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_19_R1.CraftServer; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@SuppressWarnings("all") +public class WPackRepository implements IPackRepository { + private PackRepository repository; + + @Override + public void reload() { + getRepository().reload(); + } + + @Override + public void reloadWorldData() { + ((CraftServer) Bukkit.getServer()).getServer().getWorldData() + .setDataPackConfig(getSelectedPacks()); + } + + private DataPackConfig getSelectedPacks() { + Collection selectedIds = getSelectedIds(); + List enabled = ImmutableList.copyOf(selectedIds); + List disabled = getAvailableIds().stream() + .filter((s) -> !selectedIds.contains(s)) + .toList(); + return new DataPackConfig(enabled, disabled); + } + + @Override + public void setSelected(Collection packs) { + getRepository().setSelected(packs); + } + + @Override + public boolean addPack(String packId) { + var repo = getRepository(); + var packs = new ArrayList<>(repo.getSelectedIds()); + if (repo.isAvailable(packId) && !packs.contains(packId)) { + packs.add(packId); + repo.setSelected(packs); + return true; + } else { + return false; + } + } + + @Override + public boolean removePack(String packId) { + var repo = getRepository(); + var packs = new ArrayList<>(repo.getSelectedIds()); + if (repo.isAvailable(packId) && packs.contains(packId)) { + packs.remove(packId); + repo.setSelected(packs); + return true; + } else { + return false; + } + } + + @Override + public Collection getAvailableIds() { + return getRepository().getAvailableIds(); + } + + @Override + public Collection getSelectedIds() { + return getRepository().getSelectedIds(); + } + + @Override + public boolean isAvailable(String packId) { + return getRepository().isAvailable(packId); + } + + private PackRepository getRepository() { + if (repository == null) + repository = ((CraftServer) Bukkit.getServer()).getHandle().getServer().getPackRepository(); + + return repository; + } +} diff --git a/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/CustomBiomeSource.java b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/CustomBiomeSource.java index 340c053cd..d510b0349 100644 --- a/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/CustomBiomeSource.java +++ b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/CustomBiomeSource.java @@ -2,6 +2,7 @@ package com.volmit.iris.core.nms.v1_19_R2; import com.mojang.serialization.Codec; import com.volmit.iris.Iris; +import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; @@ -148,6 +149,7 @@ public class CustomBiomeSource extends BiomeSource { } } } + ServerConfigurator.dumpDataPack(); return m; } diff --git a/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java index e189057c4..4d9017717 100644 --- a/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java +++ b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java @@ -1,11 +1,7 @@ package com.volmit.iris.core.nms.v1_19_R2; import java.awt.Color; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; +import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -17,8 +13,12 @@ import java.util.Vector; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; +import java.util.stream.Collectors; +import com.google.gson.JsonNull; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.IPackRepository; +import com.volmit.iris.util.io.IO; import net.minecraft.world.level.LevelReader; import com.google.common.base.Preconditions; import com.google.gson.JsonElement; @@ -102,6 +102,8 @@ import sun.misc.Unsafe; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); + private final WPackRepository packRepository = new WPackRepository(); + private final KMap, Boolean> changedRegistries = new KMap<>(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); @@ -625,6 +627,7 @@ public class NMSBinding implements INMSBinding { try { var holder = registry.register(key, value, Lifecycle.stable()); if (frozen) valueField.set(holder, value); + changedRegistries.put(registryKey, true); return true; } finally { field.setBoolean(registry, frozen); @@ -661,6 +664,7 @@ public class NMSBinding implements INMSBinding { toId.put(value, toId.removeInt(oldValue)); byValue.put(value, byValue.remove(oldValue)); lifecycles.put(value, lifecycles.remove(oldValue)); + changedRegistries.put(registryKey, true); return true; } catch (Throwable e) { throw new IllegalStateException(e); @@ -739,6 +743,71 @@ public class NMSBinding implements INMSBinding { } } + @Override + public boolean dumpRegistry(File... folders) { + var biomes = collect(Registries.BIOME, net.minecraft.world.level.biome.Biome.DIRECT_CODEC); + var dimensions = collect(Registries.DIMENSION_TYPE, DimensionType.DIRECT_CODEC); + + if (biomes.isEmpty() && dimensions.isEmpty()) + return false; + + for (File folder : folders) { + if (folder.getName().equals("datapacks")) + folder = new File(folder, "iris"); + File data = new File(folder, "data"); + + for (var entry : biomes.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/worldgen/biome/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write biome " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + for (var entry : dimensions.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/dimension_type/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write dimension " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + File meta = new File(folder, "pack.mcmeta"); + if (!meta.getParentFile().exists() && !meta.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(meta, "{\"pack\": {\"pack_format\": "+getDataVersion().getPackFormat()+", \"description\": \"Iris Data Pack. This pack contains all installed Iris Packs' resources.\"}}"); + } catch (IOException e) { + Iris.error("Failed to write pack.mcmeta to " + meta.getPath()); + } + } + + return true; + } + + private Map collect(ResourceKey> registryKey, Codec codec) { + var registry = registry().registry(registryKey).orElse(null); + if (registry == null || !changedRegistries.getOrDefault(registryKey, false)) + return Map.of(); + try { + return registry + .registryKeySet() + .stream() + .collect(Collectors.toMap(ResourceKey::location, id -> encode(codec, registry.get(id)).orElse(JsonNull.INSTANCE))); + } finally { + changedRegistries.put(registryKey, false); + } + } + public void injectBukkit() { try { Iris.info("Injecting Bukkit"); @@ -763,6 +832,11 @@ public class NMSBinding implements INMSBinding { } + @Override + public IPackRepository getPackRepository() { + return packRepository; + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { diff --git a/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/WPackRepository.java b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/WPackRepository.java new file mode 100644 index 000000000..4320696f9 --- /dev/null +++ b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/WPackRepository.java @@ -0,0 +1,93 @@ +package com.volmit.iris.core.nms.v1_19_R2; + +import com.google.common.collect.ImmutableList; +import com.volmit.iris.core.nms.container.IPackRepository; +import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.world.level.DataPackConfig; +import net.minecraft.world.level.WorldDataConfiguration; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_19_R2.CraftServer; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@SuppressWarnings("all") +public class WPackRepository implements IPackRepository { + private PackRepository repository; + + @Override + public void reload() { + getRepository().reload(); + } + + @Override + public void reloadWorldData() { + var worldData = ((CraftServer) Bukkit.getServer()).getServer().getWorldData(); + var config = new WorldDataConfiguration(getSelectedPacks(), worldData.enabledFeatures()); + worldData.setDataConfiguration(config); + } + + private DataPackConfig getSelectedPacks() { + Collection selectedIds = getSelectedIds(); + List enabled = ImmutableList.copyOf(selectedIds); + List disabled = getAvailableIds().stream() + .filter((s) -> !selectedIds.contains(s)) + .toList(); + return new DataPackConfig(enabled, disabled); + } + + @Override + public void setSelected(Collection packs) { + getRepository().setSelected(packs); + } + + @Override + public boolean addPack(String packId) { + var repo = getRepository(); + var packs = new ArrayList<>(repo.getSelectedIds()); + if (repo.isAvailable(packId) && !packs.contains(packId)) { + packs.add(packId); + repo.setSelected(packs); + return true; + } else { + return false; + } + } + + @Override + public boolean removePack(String packId) { + var repo = getRepository(); + var packs = new ArrayList<>(repo.getSelectedIds()); + if (repo.isAvailable(packId) && packs.contains(packId)) { + packs.remove(packId); + repo.setSelected(packs); + return true; + } else { + return false; + } + } + + @Override + public Collection getAvailableIds() { + return getRepository().getAvailableIds(); + } + + @Override + public Collection getSelectedIds() { + return getRepository().getSelectedIds(); + } + + @Override + public boolean isAvailable(String packId) { + return getRepository().isAvailable(packId); + } + + private PackRepository getRepository() { + if (repository == null) + repository = ((CraftServer) Bukkit.getServer()).getHandle().getServer().getPackRepository(); + return repository; + } +} diff --git a/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/CustomBiomeSource.java b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/CustomBiomeSource.java index 3e26f6f79..335140718 100644 --- a/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/CustomBiomeSource.java +++ b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/CustomBiomeSource.java @@ -2,6 +2,7 @@ package com.volmit.iris.core.nms.v1_19_R3; import com.mojang.serialization.Codec; import com.volmit.iris.Iris; +import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; @@ -151,6 +152,7 @@ public class CustomBiomeSource extends BiomeSource { } } } + ServerConfigurator.dumpDataPack(); return m; } diff --git a/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java index 3b44c0ef8..d02a82079 100644 --- a/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java +++ b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java @@ -1,11 +1,7 @@ package com.volmit.iris.core.nms.v1_19_R3; import java.awt.Color; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; +import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -17,8 +13,12 @@ import java.util.Vector; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; +import java.util.stream.Collectors; +import com.google.gson.JsonNull; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.IPackRepository; +import com.volmit.iris.util.io.IO; import net.minecraft.world.level.LevelReader; import com.google.common.base.Preconditions; import com.google.gson.JsonElement; @@ -103,6 +103,8 @@ import sun.misc.Unsafe; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); + private final WPackRepository packRepository = new WPackRepository(); + private final KMap, Boolean> changedRegistries = new KMap<>(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); @@ -629,6 +631,7 @@ public class NMSBinding implements INMSBinding { try { var holder = registry.register(key, value, Lifecycle.stable()); if (frozen) valueField.set(holder, value); + changedRegistries.put(registryKey, true); return true; } finally { field.setBoolean(registry, frozen); @@ -665,6 +668,7 @@ public class NMSBinding implements INMSBinding { toId.put(value, toId.removeInt(oldValue)); byValue.put(value, byValue.remove(oldValue)); lifecycles.put(value, lifecycles.remove(oldValue)); + changedRegistries.put(registryKey, true); return true; } catch (Throwable e) { throw new IllegalStateException(e); @@ -743,6 +747,71 @@ public class NMSBinding implements INMSBinding { } } + @Override + public boolean dumpRegistry(File... folders) { + var biomes = collect(Registries.BIOME, net.minecraft.world.level.biome.Biome.DIRECT_CODEC); + var dimensions = collect(Registries.DIMENSION_TYPE, DimensionType.DIRECT_CODEC); + + if (biomes.isEmpty() && dimensions.isEmpty()) + return false; + + for (File folder : folders) { + if (folder.getName().equals("datapacks")) + folder = new File(folder, "iris"); + File data = new File(folder, "data"); + + for (var entry : biomes.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/worldgen/biome/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write biome " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + for (var entry : dimensions.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/dimension_type/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write dimension " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + File meta = new File(folder, "pack.mcmeta"); + if (!meta.getParentFile().exists() && !meta.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(meta, "{\"pack\": {\"pack_format\": "+getDataVersion().getPackFormat()+", \"description\": \"Iris Data Pack. This pack contains all installed Iris Packs' resources.\"}}"); + } catch (IOException e) { + Iris.error("Failed to write pack.mcmeta to " + meta.getPath()); + } + } + + return true; + } + + private Map collect(ResourceKey> registryKey, Codec codec) { + var registry = registry().registry(registryKey).orElse(null); + if (registry == null || !changedRegistries.getOrDefault(registryKey, false)) + return Map.of(); + try { + return registry + .registryKeySet() + .stream() + .collect(Collectors.toMap(ResourceKey::location, id -> encode(codec, registry.get(id)).orElse(JsonNull.INSTANCE))); + } finally { + changedRegistries.put(registryKey, false); + } + } + public void injectBukkit() { try { Iris.info("Injecting Bukkit"); @@ -767,6 +836,11 @@ public class NMSBinding implements INMSBinding { } + @Override + public IPackRepository getPackRepository() { + return packRepository; + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { diff --git a/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/WPackRepository.java b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/WPackRepository.java new file mode 100644 index 000000000..5c33f2c78 --- /dev/null +++ b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/WPackRepository.java @@ -0,0 +1,76 @@ +package com.volmit.iris.core.nms.v1_19_R3; + +import com.google.common.collect.ImmutableList; +import com.volmit.iris.core.nms.container.IPackRepository; +import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.world.level.DataPackConfig; +import net.minecraft.world.level.WorldDataConfiguration; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_19_R3.CraftServer; +import org.bukkit.craftbukkit.v1_19_R3.packs.CraftDataPackManager; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@SuppressWarnings("all") +public class WPackRepository implements IPackRepository { + private PackRepository repository; + + @Override + public void reload() { + getRepository().reload(); + } + + @Override + public void reloadWorldData() { + var worldData = ((CraftServer) Bukkit.getServer()).getServer().getWorldData(); + var config = new WorldDataConfiguration(getSelectedPacks(), worldData.enabledFeatures()); + worldData.setDataConfiguration(config); + } + + private DataPackConfig getSelectedPacks() { + Collection selectedIds = getSelectedIds(); + List enabled = ImmutableList.copyOf(selectedIds); + List disabled = getAvailableIds().stream() + .filter((s) -> !selectedIds.contains(s)) + .toList(); + return new DataPackConfig(enabled, disabled); + } + + @Override + public void setSelected(Collection packs) { + getRepository().setSelected(packs); + } + + @Override + public boolean addPack(String packId) { + return getRepository().addPack(packId); + } + + @Override + public boolean removePack(String packId) { + return getRepository().removePack(packId); + } + + @Override + public Collection getAvailableIds() { + return getRepository().getAvailableIds(); + } + + @Override + public Collection getSelectedIds() { + return getRepository().getSelectedIds(); + } + + @Override + public boolean isAvailable(String packId) { + return getRepository().isAvailable(packId); + } + + private PackRepository getRepository() { + if (repository == null) + repository = ((CraftDataPackManager) Bukkit.getDataPackManager()).getHandle(); + return repository; + } +} diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/CustomBiomeSource.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/CustomBiomeSource.java index 6ebea1093..22cf176cd 100644 --- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/CustomBiomeSource.java +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/CustomBiomeSource.java @@ -2,6 +2,7 @@ package com.volmit.iris.core.nms.v1_20_R1; import com.mojang.serialization.Codec; import com.volmit.iris.Iris; +import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; @@ -151,6 +152,7 @@ public class CustomBiomeSource extends BiomeSource { } } } + ServerConfigurator.dumpDataPack(); return m; } diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java index c90ca78b0..87a9a8026 100644 --- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java @@ -2,6 +2,7 @@ package com.volmit.iris.core.nms.v1_20_R1; import com.google.common.base.Preconditions; import com.google.gson.JsonElement; +import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.datafixers.util.Pair; @@ -11,6 +12,7 @@ import com.mojang.serialization.Lifecycle; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.IPackRepository; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.IrisBiomeCustom; @@ -19,6 +21,7 @@ import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.format.C; import com.volmit.iris.util.hunk.Hunk; +import com.volmit.iris.util.io.IO; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.math.Vector3d; @@ -83,11 +86,7 @@ import org.jetbrains.annotations.NotNull; import sun.misc.Unsafe; import java.awt.Color; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; +import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -99,12 +98,15 @@ import java.util.Vector; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; +import java.util.stream.Collectors; public class NMSBinding implements INMSBinding { public static final String NMS_VERSION = "1.20.1"; private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); + private final WPackRepository packRepository = new WPackRepository(); + private final KMap, Boolean> changedRegistries = new KMap<>(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); @@ -614,6 +616,7 @@ public class NMSBinding implements INMSBinding { try { var holder = registry.register(key, value, Lifecycle.stable()); if (frozen) valueField.set(holder, value); + changedRegistries.put(registryKey, true); return true; } finally { field.setBoolean(registry, frozen); @@ -650,6 +653,7 @@ public class NMSBinding implements INMSBinding { toId.put(value, toId.removeInt(oldValue)); byValue.put(value, byValue.remove(oldValue)); lifecycles.put(value, lifecycles.remove(oldValue)); + changedRegistries.put(registryKey, true); return true; } catch (Throwable e) { throw new IllegalStateException(e); @@ -741,6 +745,71 @@ public class NMSBinding implements INMSBinding { } } + @Override + public boolean dumpRegistry(File... folders) { + var biomes = collect(Registries.BIOME, net.minecraft.world.level.biome.Biome.DIRECT_CODEC); + var dimensions = collect(Registries.DIMENSION_TYPE, DimensionType.DIRECT_CODEC); + + if (biomes.isEmpty() && dimensions.isEmpty()) + return false; + + for (File folder : folders) { + if (folder.getName().equals("datapacks")) + folder = new File(folder, "iris"); + File data = new File(folder, "data"); + + for (var entry : biomes.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/worldgen/biome/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write biome " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + for (var entry : dimensions.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/dimension_type/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write dimension " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + File meta = new File(folder, "pack.mcmeta"); + if (!meta.getParentFile().exists() && !meta.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(meta, "{\"pack\": {\"pack_format\": "+getDataVersion().getPackFormat()+", \"description\": \"Iris Data Pack. This pack contains all installed Iris Packs' resources.\"}}"); + } catch (IOException e) { + Iris.error("Failed to write pack.mcmeta to " + meta.getPath()); + } + } + + return true; + } + + private Map collect(ResourceKey> registryKey, Codec codec) { + var registry = registry().registry(registryKey).orElse(null); + if (registry == null || !changedRegistries.getOrDefault(registryKey, false)) + return Map.of(); + try { + return registry + .registryKeySet() + .stream() + .collect(Collectors.toMap(ResourceKey::location, id -> encode(codec, registry.get(id)).orElse(JsonNull.INSTANCE))); + } finally { + changedRegistries.put(registryKey, false); + } + } + public void injectBukkit() { try { Iris.info("Injecting Bukkit"); @@ -765,6 +834,11 @@ public class NMSBinding implements INMSBinding { } + @Override + public IPackRepository getPackRepository() { + return packRepository; + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/WPackRepository.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/WPackRepository.java new file mode 100644 index 000000000..ddf8d71fa --- /dev/null +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/WPackRepository.java @@ -0,0 +1,76 @@ +package com.volmit.iris.core.nms.v1_20_R1; + +import com.google.common.collect.ImmutableList; +import com.volmit.iris.core.nms.container.IPackRepository; +import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.world.level.DataPackConfig; +import net.minecraft.world.level.WorldDataConfiguration; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_20_R1.CraftServer; +import org.bukkit.craftbukkit.v1_20_R1.packs.CraftDataPackManager; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@SuppressWarnings("all") +public class WPackRepository implements IPackRepository { + private PackRepository repository; + + @Override + public void reload() { + getRepository().reload(); + } + + @Override + public void reloadWorldData() { + var worldData = ((CraftServer) Bukkit.getServer()).getServer().getWorldData(); + var config = new WorldDataConfiguration(getSelectedPacks(), worldData.enabledFeatures()); + worldData.setDataConfiguration(config); + } + + private DataPackConfig getSelectedPacks() { + Collection selectedIds = getSelectedIds(); + List enabled = ImmutableList.copyOf(selectedIds); + List disabled = getAvailableIds().stream() + .filter((s) -> !selectedIds.contains(s)) + .toList(); + return new DataPackConfig(enabled, disabled); + } + + @Override + public void setSelected(Collection packs) { + getRepository().setSelected(packs); + } + + @Override + public boolean addPack(String packId) { + return getRepository().addPack(packId); + } + + @Override + public boolean removePack(String packId) { + return getRepository().removePack(packId); + } + + @Override + public Collection getAvailableIds() { + return getRepository().getAvailableIds(); + } + + @Override + public Collection getSelectedIds() { + return getRepository().getSelectedIds(); + } + + @Override + public boolean isAvailable(String packId) { + return getRepository().isAvailable(packId); + } + + private PackRepository getRepository() { + if (repository == null) + repository = ((CraftDataPackManager) Bukkit.getDataPackManager()).getHandle(); + return repository; + } +} diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/CustomBiomeSource.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/CustomBiomeSource.java index 24ce23216..cc7acc30a 100644 --- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/CustomBiomeSource.java +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/CustomBiomeSource.java @@ -2,6 +2,7 @@ package com.volmit.iris.core.nms.v1_20_R2; import com.mojang.serialization.Codec; import com.volmit.iris.Iris; +import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; @@ -150,6 +151,7 @@ public class CustomBiomeSource extends BiomeSource { } } } + ServerConfigurator.dumpDataPack(); return m; } diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java index 378c7c9fc..a2e44d8ef 100644 --- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java @@ -1,11 +1,7 @@ package com.volmit.iris.core.nms.v1_20_R2; import java.awt.Color; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; +import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -17,8 +13,13 @@ import java.util.Vector; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; +import java.util.stream.Collectors; +import com.google.gson.JsonNull; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.IPackRepository; +import com.volmit.iris.util.io.IO; +import net.minecraft.server.commands.DataPackCommand; import net.minecraft.world.level.LevelReader; import com.google.common.base.Preconditions; import com.google.gson.JsonElement; @@ -54,6 +55,7 @@ import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R2.entity.CraftDolphin; import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_20_R2.packs.CraftDataPackManager; import org.bukkit.craftbukkit.v1_20_R2.util.CraftNamespacedKey; import org.bukkit.entity.Dolphin; import org.bukkit.entity.Entity; @@ -104,6 +106,8 @@ import sun.misc.Unsafe; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); + private final WPackRepository packRepository = new WPackRepository(); + private final KMap, Boolean> changedRegistries = new KMap<>(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); @@ -626,6 +630,7 @@ public class NMSBinding implements INMSBinding { try { var holder = registry.register(key, value, Lifecycle.stable()); if (frozen) valueField.set(holder, value); + changedRegistries.put(registryKey, true); return true; } finally { field.setBoolean(registry, frozen); @@ -662,6 +667,7 @@ public class NMSBinding implements INMSBinding { toId.put(value, toId.removeInt(oldValue)); byValue.put(value, byValue.remove(oldValue)); lifecycles.put(value, lifecycles.remove(oldValue)); + changedRegistries.put(registryKey, true); return true; } catch (Throwable e) { throw new IllegalStateException(e); @@ -745,6 +751,71 @@ public class NMSBinding implements INMSBinding { return registry.getHolderOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey()))); } + @Override + public boolean dumpRegistry(File... folders) { + var biomes = collect(Registries.BIOME, net.minecraft.world.level.biome.Biome.DIRECT_CODEC); + var dimensions = collect(Registries.DIMENSION_TYPE, DimensionType.DIRECT_CODEC); + + if (biomes.isEmpty() && dimensions.isEmpty()) + return false; + + for (File folder : folders) { + if (folder.getName().equals("datapacks")) + folder = new File(folder, "iris"); + File data = new File(folder, "data"); + + for (var entry : biomes.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/worldgen/biome/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write biome " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + for (var entry : dimensions.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/dimension_type/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write dimension " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + File meta = new File(folder, "pack.mcmeta"); + if (!meta.getParentFile().exists() && !meta.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(meta, "{\"pack\": {\"pack_format\": "+getDataVersion().getPackFormat()+", \"description\": \"Iris Data Pack. This pack contains all installed Iris Packs' resources.\"}}"); + } catch (IOException e) { + Iris.error("Failed to write pack.mcmeta to " + meta.getPath()); + } + } + + return true; + } + + private Map collect(ResourceKey> registryKey, Codec codec) { + var registry = registry().registry(registryKey).orElse(null); + if (registry == null || !changedRegistries.getOrDefault(registryKey, false)) + return Map.of(); + try { + return registry + .registryKeySet() + .stream() + .collect(Collectors.toMap(ResourceKey::location, id -> encode(codec, registry.get(id)).orElse(JsonNull.INSTANCE))); + } finally { + changedRegistries.put(registryKey, false); + } + } + public void injectBukkit() { try { Iris.info("Injecting Bukkit"); @@ -769,6 +840,11 @@ public class NMSBinding implements INMSBinding { } + @Override + public IPackRepository getPackRepository() { + return packRepository; + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/WPackRepository.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/WPackRepository.java new file mode 100644 index 000000000..41b627d8e --- /dev/null +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/WPackRepository.java @@ -0,0 +1,76 @@ +package com.volmit.iris.core.nms.v1_20_R2; + +import com.google.common.collect.ImmutableList; +import com.volmit.iris.core.nms.container.IPackRepository; +import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.world.level.DataPackConfig; +import net.minecraft.world.level.WorldDataConfiguration; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_20_R2.CraftServer; +import org.bukkit.craftbukkit.v1_20_R2.packs.CraftDataPackManager; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@SuppressWarnings("all") +public class WPackRepository implements IPackRepository { + private PackRepository repository; + + @Override + public void reload() { + getRepository().reload(); + } + + @Override + public void reloadWorldData() { + var worldData = ((CraftServer) Bukkit.getServer()).getServer().getWorldData(); + var config = new WorldDataConfiguration(getSelectedPacks(), worldData.enabledFeatures()); + worldData.setDataConfiguration(config); + } + + private DataPackConfig getSelectedPacks() { + Collection selectedIds = getSelectedIds(); + List enabled = ImmutableList.copyOf(selectedIds); + List disabled = getAvailableIds().stream() + .filter((s) -> !selectedIds.contains(s)) + .toList(); + return new DataPackConfig(enabled, disabled); + } + + @Override + public void setSelected(Collection packs) { + getRepository().setSelected(packs); + } + + @Override + public boolean addPack(String packId) { + return getRepository().addPack(packId); + } + + @Override + public boolean removePack(String packId) { + return getRepository().removePack(packId); + } + + @Override + public Collection getAvailableIds() { + return getRepository().getAvailableIds(); + } + + @Override + public Collection getSelectedIds() { + return getRepository().getSelectedIds(); + } + + @Override + public boolean isAvailable(String packId) { + return getRepository().isAvailable(packId); + } + + private PackRepository getRepository() { + if (repository == null) + repository = ((CraftDataPackManager) Bukkit.getDataPackManager()).getHandle(); + return repository; + } +} diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/CustomBiomeSource.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/CustomBiomeSource.java index 323fa255b..0cfb73c64 100644 --- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/CustomBiomeSource.java +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/CustomBiomeSource.java @@ -2,6 +2,7 @@ package com.volmit.iris.core.nms.v1_20_R3; import com.mojang.serialization.Codec; import com.volmit.iris.Iris; +import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; @@ -150,6 +151,7 @@ public class CustomBiomeSource extends BiomeSource { } } } + ServerConfigurator.dumpDataPack(); return m; } diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/Headless.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/Headless.java index cc3ae205e..510f604e5 100644 --- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/Headless.java +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/Headless.java @@ -1,6 +1,7 @@ package com.volmit.iris.core.nms.v1_20_R3; import com.volmit.iris.Iris; +import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.nms.BiomeBaseInjector; import com.volmit.iris.core.nms.IHeadless; import com.volmit.iris.core.nms.v1_20_R3.mca.MCATerrainChunk; @@ -74,6 +75,7 @@ public class Headless implements IHeadless, LevelHeightAccessor { binding.registerBiome(dimKey, custom, false); } } + ServerConfigurator.dumpDataPack(); } /** diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java index 4f9384fbf..e1b5162e9 100644 --- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java @@ -1,11 +1,7 @@ package com.volmit.iris.core.nms.v1_20_R3; import java.awt.Color; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; +import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -13,19 +9,23 @@ import java.util.*; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; +import java.util.stream.Collectors; import com.google.common.base.Preconditions; import com.google.gson.JsonElement; +import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import com.mojang.serialization.Lifecycle; import com.volmit.iris.core.nms.IHeadless; +import com.volmit.iris.core.nms.container.IPackRepository; import com.volmit.iris.core.nms.v1_20_R3.mca.ChunkSerializer; import com.volmit.iris.engine.object.IrisBiomeCustom; import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.format.C; +import com.volmit.iris.util.io.IO; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; @@ -114,6 +114,8 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); + private final WPackRepository packRepository = new WPackRepository(); + private final KMap, Boolean> changedRegistries = new KMap<>(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); @@ -636,6 +638,7 @@ public class NMSBinding implements INMSBinding { try { var holder = registry.register(key, value, Lifecycle.stable()); if (frozen) valueField.set(holder, value); + changedRegistries.put(registryKey, true); return true; } finally { field.setBoolean(registry, frozen); @@ -672,6 +675,7 @@ public class NMSBinding implements INMSBinding { toId.put(value, toId.removeInt(oldValue)); byValue.put(value, byValue.remove(oldValue)); lifecycles.put(value, lifecycles.remove(oldValue)); + changedRegistries.put(registryKey, true); return true; } catch (Throwable e) { throw new IllegalStateException(e); @@ -754,6 +758,71 @@ public class NMSBinding implements INMSBinding { return registry.getHolderOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey()))); } + @Override + public boolean dumpRegistry(File... folders) { + var biomes = collect(Registries.BIOME, net.minecraft.world.level.biome.Biome.DIRECT_CODEC); + var dimensions = collect(Registries.DIMENSION_TYPE, DimensionType.DIRECT_CODEC); + + if (biomes.isEmpty() && dimensions.isEmpty()) + return false; + + for (File folder : folders) { + if (folder.getName().equals("datapacks")) + folder = new File(folder, "iris"); + File data = new File(folder, "data"); + + for (var entry : biomes.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/worldgen/biome/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write biome " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + for (var entry : dimensions.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/dimension_type/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write dimension " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + File meta = new File(folder, "pack.mcmeta"); + if (!meta.getParentFile().exists() && !meta.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(meta, "{\"pack\": {\"pack_format\": "+getDataVersion().getPackFormat()+", \"description\": \"Iris Data Pack. This pack contains all installed Iris Packs' resources.\"}}"); + } catch (IOException e) { + Iris.error("Failed to write pack.mcmeta to " + meta.getPath()); + } + } + + return true; + } + + private Map collect(ResourceKey> registryKey, Codec codec) { + var registry = registry().registry(registryKey).orElse(null); + if (registry == null || !changedRegistries.getOrDefault(registryKey, false)) + return Map.of(); + try { + return registry + .registryKeySet() + .stream() + .collect(Collectors.toMap(ResourceKey::location, id -> encode(codec, registry.get(id)).orElse(JsonNull.INSTANCE))); + } finally { + changedRegistries.put(registryKey, false); + } + } + public void injectBukkit() { try { Iris.info("Injecting Bukkit"); @@ -784,6 +853,11 @@ public class NMSBinding implements INMSBinding { return new Headless(this, engine); } + @Override + public IPackRepository getPackRepository() { + return packRepository; + } + private static class ServerLevelAdvice { @Advice.OnMethodEnter static void enter(@Advice.Argument(0) MinecraftServer server, @Advice.Argument(2) LevelStorageSource.LevelStorageAccess access, @Advice.Argument(4) ResourceKey key, @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem) { diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/WPackRepository.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/WPackRepository.java new file mode 100644 index 000000000..353b018ee --- /dev/null +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/WPackRepository.java @@ -0,0 +1,76 @@ +package com.volmit.iris.core.nms.v1_20_R3; + +import com.google.common.collect.ImmutableList; +import com.volmit.iris.core.nms.container.IPackRepository; +import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.world.level.DataPackConfig; +import net.minecraft.world.level.WorldDataConfiguration; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_20_R3.CraftServer; +import org.bukkit.craftbukkit.v1_20_R3.packs.CraftDataPackManager; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@SuppressWarnings("all") +public class WPackRepository implements IPackRepository { + private PackRepository repository; + + @Override + public void reload() { + getRepository().reload(); + } + + @Override + public void reloadWorldData() { + var worldData = ((CraftServer) Bukkit.getServer()).getServer().getWorldData(); + var config = new WorldDataConfiguration(getSelectedPacks(), worldData.enabledFeatures()); + worldData.setDataConfiguration(config); + } + + private DataPackConfig getSelectedPacks() { + Collection selectedIds = getSelectedIds(); + List enabled = ImmutableList.copyOf(selectedIds); + List disabled = getAvailableIds().stream() + .filter((s) -> !selectedIds.contains(s)) + .toList(); + return new DataPackConfig(enabled, disabled); + } + + @Override + public void setSelected(Collection packs) { + getRepository().setSelected(packs); + } + + @Override + public boolean addPack(String packId) { + return getRepository().addPack(packId); + } + + @Override + public boolean removePack(String packId) { + return getRepository().removePack(packId); + } + + @Override + public Collection getAvailableIds() { + return getRepository().getAvailableIds(); + } + + @Override + public Collection getSelectedIds() { + return getRepository().getSelectedIds(); + } + + @Override + public boolean isAvailable(String packId) { + return getRepository().isAvailable(packId); + } + + private PackRepository getRepository() { + if (repository == null) + repository = ((CraftDataPackManager) Bukkit.getDataPackManager()).getHandle(); + return repository; + } +} diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/CustomBiomeSource.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/CustomBiomeSource.java index a27c9a306..3e2ded314 100644 --- a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/CustomBiomeSource.java +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/CustomBiomeSource.java @@ -2,6 +2,7 @@ package com.volmit.iris.core.nms.v1_20_R4; import com.mojang.serialization.MapCodec; import com.volmit.iris.Iris; +import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; @@ -150,6 +151,7 @@ public class CustomBiomeSource extends BiomeSource { } } } + ServerConfigurator.dumpDataPack(); return m; } diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java index dd776ad0d..13beaadc0 100644 --- a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java @@ -1,11 +1,7 @@ package com.volmit.iris.core.nms.v1_20_R4; import java.awt.Color; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; +import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -17,7 +13,9 @@ import java.util.Vector; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; +import java.util.stream.Collectors; +import com.google.gson.JsonNull; import com.volmit.iris.core.nms.container.BiomeColor; import com.google.common.base.Preconditions; import com.google.gson.JsonElement; @@ -25,10 +23,12 @@ import com.google.gson.JsonObject; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; +import com.volmit.iris.core.nms.container.IPackRepository; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.object.IrisBiomeCustom; import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.format.C; +import com.volmit.iris.util.io.IO; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; @@ -107,6 +107,8 @@ import sun.misc.Unsafe; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); + private final WPackRepository packRepository = new WPackRepository(); + private final KMap, Boolean> changedRegistries = new KMap<>(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); @@ -667,6 +669,7 @@ public class NMSBinding implements INMSBinding { try { var holder = registry.register(key, value, RegistrationInfo.BUILT_IN); if (frozen) valueField.set(holder, value); + changedRegistries.put(registryKey, true); return true; } finally { field.setBoolean(registry, frozen); @@ -699,6 +702,7 @@ public class NMSBinding implements INMSBinding { valueField.set(holder, value); toId.put(value, toId.removeInt(oldValue)); byValue.put(value, byValue.remove(oldValue)); + changedRegistries.put(registryKey, true); return true; } catch (Throwable e) { throw new IllegalStateException(e); @@ -736,6 +740,84 @@ public class NMSBinding implements INMSBinding { } } + @Override + public boolean dumpRegistry(File... folders) { + var biomes = collect(Registries.BIOME, net.minecraft.world.level.biome.Biome.DIRECT_CODEC); + var dimensions = collect(Registries.DIMENSION_TYPE, DimensionType.DIRECT_CODEC); + + if (biomes.isEmpty() && dimensions.isEmpty()) + return false; + + for (File folder : folders) { + if (folder.getName().equals("datapacks")) + folder = new File(folder, "iris"); + File data = new File(folder, "data"); + + for (var entry : biomes.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/worldgen/biome/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write biome " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + for (var entry : dimensions.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/dimension_type/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write dimension " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + File meta = new File(folder, "pack.mcmeta"); + if (!meta.getParentFile().exists() && !meta.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(meta, "{\"pack\": {\"pack_format\": "+getDataVersion().getPackFormat()+", \"description\": \"Iris Data Pack. This pack contains all installed Iris Packs' resources.\"}}"); + } catch (IOException e) { + Iris.error("Failed to write pack.mcmeta to " + meta.getPath()); + } + } + + return true; + } + + private Map collect(ResourceKey> registryKey, Codec codec) { + var registry = registry().registry(registryKey).orElse(null); + if (registry == null || !changedRegistries.getOrDefault(registryKey, false)) + return Map.of(); + try { + return registry + .registryKeySet() + .stream() + .collect(Collectors.toMap(ResourceKey::location, id -> encode(codec, registry.get(id)).orElse(JsonNull.INSTANCE))); + } finally { + changedRegistries.put(registryKey, false); + } + } + + @Override + public int getSpawnChunkCount(World world) { + var radius = Optional.ofNullable(world.getGameRuleValue(GameRule.SPAWN_CHUNK_RADIUS)) + .orElseGet(() -> world.getGameRuleDefault(GameRule.SPAWN_CHUNK_RADIUS)); + if (radius == null) throw new IllegalStateException("GameRule.SPAWN_CHUNK_RADIUS is null!"); + return (int) Math.pow(2 * radius + 1, 2); + } + + @Override + public IPackRepository getPackRepository() { + return packRepository; + } + public void injectBukkit() { try { Iris.info("Injecting Bukkit"); diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/WPackRepository.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/WPackRepository.java new file mode 100644 index 000000000..829c311ff --- /dev/null +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/WPackRepository.java @@ -0,0 +1,76 @@ +package com.volmit.iris.core.nms.v1_20_R4; + +import com.google.common.collect.ImmutableList; +import com.volmit.iris.core.nms.container.IPackRepository; +import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.world.level.DataPackConfig; +import net.minecraft.world.level.WorldDataConfiguration; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_20_R4.CraftServer; +import org.bukkit.craftbukkit.v1_20_R4.packs.CraftDataPackManager; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@SuppressWarnings("all") +public class WPackRepository implements IPackRepository { + private PackRepository repository; + + @Override + public void reload() { + getRepository().reload(); + } + + @Override + public void reloadWorldData() { + var worldData = ((CraftServer) Bukkit.getServer()).getServer().getWorldData(); + var config = new WorldDataConfiguration(getSelectedPacks(), worldData.enabledFeatures()); + worldData.setDataConfiguration(config); + } + + private DataPackConfig getSelectedPacks() { + Collection selectedIds = getSelectedIds(); + List enabled = ImmutableList.copyOf(selectedIds); + List disabled = getAvailableIds().stream() + .filter((s) -> !selectedIds.contains(s)) + .toList(); + return new DataPackConfig(enabled, disabled); + } + + @Override + public void setSelected(Collection packs) { + getRepository().setSelected(packs); + } + + @Override + public boolean addPack(String packId) { + return getRepository().addPack(packId); + } + + @Override + public boolean removePack(String packId) { + return getRepository().removePack(packId); + } + + @Override + public Collection getAvailableIds() { + return getRepository().getAvailableIds(); + } + + @Override + public Collection getSelectedIds() { + return getRepository().getSelectedIds(); + } + + @Override + public boolean isAvailable(String packId) { + return getRepository().isAvailable(packId); + } + + private PackRepository getRepository() { + if (repository == null) + repository = ((CraftDataPackManager) Bukkit.getDataPackManager()).getHandle(); + return repository; + } +} diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/CustomBiomeSource.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/CustomBiomeSource.java index 464b9f57f..97737d084 100644 --- a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/CustomBiomeSource.java +++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/CustomBiomeSource.java @@ -2,6 +2,8 @@ package com.volmit.iris.core.nms.v1_21_R1; import com.mojang.serialization.MapCodec; import com.volmit.iris.Iris; +import com.volmit.iris.core.ServerConfigurator; +import com.volmit.iris.core.nms.INMS; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.IrisBiome; @@ -124,8 +126,16 @@ public class CustomBiomeSource extends BiomeSource { for (IrisBiome i : engine.getAllBiomes()) { if (i.isCustom()) { for (IrisBiomeCustom j : i.getCustomDerivitives()) { - ResourceLocation resourceLocation = ResourceLocation.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId()); - Biome biome = customRegistry.get(resourceLocation); + ResourceLocation location = ResourceLocation.fromNamespaceAndPath(engine.getDimension().getLoadKey(), j.getId()); + Biome biome = customRegistry.get(location); + if (biome == null) { + INMS.get().registerBiome(location.getNamespace(), j, false); + biome = customRegistry.get(location); + if (biome == null) { + Iris.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName()); + continue; + } + } Optional> optionalBiomeKey = customRegistry.getResourceKey(biome); if (optionalBiomeKey.isEmpty()) { Iris.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName()); @@ -141,6 +151,7 @@ public class CustomBiomeSource extends BiomeSource { } } } + ServerConfigurator.dumpDataPack(); return m; } diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java index bd0d1dc03..37433ccef 100644 --- a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java +++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java @@ -9,18 +9,22 @@ import java.util.*; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; +import java.util.stream.Collectors; import com.google.common.base.Preconditions; import com.google.gson.JsonElement; +import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.IPackRepository; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.object.IrisBiomeCustom; import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.util.format.C; +import com.volmit.iris.util.io.IO; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; @@ -97,6 +101,8 @@ import sun.misc.Unsafe; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); private final BlockData AIR = Material.AIR.createBlockData(); + private final WPackRepository packRepository = new WPackRepository(); + private final KMap, Boolean> changedRegistries = new KMap<>(); private final AtomicCache> biomeMapCache = new AtomicCache<>(); private final AtomicCache> registryCache = new AtomicCache<>(); private final AtomicCache> globalCache = new AtomicCache<>(); @@ -660,6 +666,7 @@ public class NMSBinding implements INMSBinding { try { var holder = registry.register(key, value, RegistrationInfo.BUILT_IN); if (frozen) valueField.set(holder, value); + changedRegistries.put(registryKey, true); return true; } finally { field.setBoolean(registry, frozen); @@ -692,6 +699,7 @@ public class NMSBinding implements INMSBinding { valueField.set(holder, value); toId.put(value, toId.removeInt(oldValue)); byValue.put(value, byValue.remove(oldValue)); + changedRegistries.put(registryKey, true); return true; } catch (Throwable e) { throw new IllegalStateException(e); @@ -729,6 +737,85 @@ public class NMSBinding implements INMSBinding { } } + @Override + public boolean dumpRegistry(File... folders) { + var biomes = collect(Registries.BIOME, net.minecraft.world.level.biome.Biome.DIRECT_CODEC); + var dimensions = collect(Registries.DIMENSION_TYPE, DimensionType.DIRECT_CODEC); + + if (biomes.isEmpty() && dimensions.isEmpty()) + return false; + + for (File folder : folders) { + if (folder.getName().equals("datapacks")) + folder = new File(folder, "iris"); + File data = new File(folder, "data"); + + for (var entry : biomes.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/worldgen/biome/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write biome " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + for (var entry : dimensions.entrySet()) { + File file = new File(data, entry.getKey().getNamespace() + "/dimension_type/" + entry.getKey().getPath() + ".json"); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(file, entry.getValue().toString()); + } catch (IOException e) { + Iris.error("Failed to write dimension " + entry.getKey().toString() + " to " + file.getPath()); + } + } + + File meta = new File(folder, "pack.mcmeta"); + if (!meta.getParentFile().exists() && !meta.getParentFile().mkdirs()) + continue; + + try { + IO.writeAll(meta, "{\"pack\": {\"pack_format\": "+getDataVersion().getPackFormat()+", \"description\": \"Iris Data Pack. This pack contains all installed Iris Packs' resources.\"}}"); + } catch (IOException e) { + Iris.error("Failed to write pack.mcmeta to " + meta.getPath()); + } + } + + return true; + } + + private Map collect(ResourceKey> registryKey, Codec codec) { + var registry = registry().registry(registryKey).orElse(null); + if (registry == null || !changedRegistries.getOrDefault(registryKey, false)) + return Map.of(); + try { + return registry + .registryKeySet() + .stream() + .filter(id -> !id.location().getNamespace().equals("minecraft")) + .collect(Collectors.toMap(ResourceKey::location, id -> encode(codec, registry.get(id)).orElse(JsonNull.INSTANCE))); + } finally { + changedRegistries.put(registryKey, false); + } + } + + @Override + public int getSpawnChunkCount(World world) { + var radius = Optional.ofNullable(world.getGameRuleValue(GameRule.SPAWN_CHUNK_RADIUS)) + .orElseGet(() -> world.getGameRuleDefault(GameRule.SPAWN_CHUNK_RADIUS)); + if (radius == null) throw new IllegalStateException("GameRule.SPAWN_CHUNK_RADIUS is null!"); + return (int) Math.pow(2 * radius + 1, 2); + } + + @Override + public IPackRepository getPackRepository() { + return packRepository; + } + public void injectBukkit() { try { Iris.info("Injecting Bukkit"); diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/WPackRepository.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/WPackRepository.java new file mode 100644 index 000000000..d68e90e0c --- /dev/null +++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/WPackRepository.java @@ -0,0 +1,76 @@ +package com.volmit.iris.core.nms.v1_21_R1; + +import com.google.common.collect.ImmutableList; +import com.volmit.iris.core.nms.container.IPackRepository; +import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.world.level.DataPackConfig; +import net.minecraft.world.level.WorldDataConfiguration; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_21_R1.CraftServer; +import org.bukkit.craftbukkit.v1_21_R1.packs.CraftDataPackManager; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@SuppressWarnings("all") +public class WPackRepository implements IPackRepository { + private PackRepository repository; + + @Override + public void reload() { + getRepository().reload(); + } + + @Override + public void reloadWorldData() { + var worldData = ((CraftServer) Bukkit.getServer()).getServer().getWorldData(); + var config = new WorldDataConfiguration(getSelectedPacks(), worldData.enabledFeatures()); + worldData.setDataConfiguration(config); + } + + private DataPackConfig getSelectedPacks() { + Collection selectedIds = getSelectedIds(); + List enabled = ImmutableList.copyOf(selectedIds); + List disabled = getAvailableIds().stream() + .filter((s) -> !selectedIds.contains(s)) + .toList(); + return new DataPackConfig(enabled, disabled); + } + + @Override + public void setSelected(Collection packs) { + getRepository().setSelected(packs); + } + + @Override + public boolean addPack(String packId) { + return getRepository().addPack(packId); + } + + @Override + public boolean removePack(String packId) { + return getRepository().removePack(packId); + } + + @Override + public Collection getAvailableIds() { + return getRepository().getAvailableIds(); + } + + @Override + public Collection getSelectedIds() { + return getRepository().getSelectedIds(); + } + + @Override + public boolean isAvailable(String packId) { + return getRepository().isAvailable(packId); + } + + private PackRepository getRepository() { + if (repository == null) + repository = ((CraftDataPackManager) Bukkit.getDataPackManager()).getHandle(); + return repository; + } +}