From de670ddfd516d0d00535330040f669f8fc662948 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sun, 11 Aug 2024 12:03:23 +0200 Subject: [PATCH] fix explorer/treasure map --- .../com/volmit/iris/core/nms/INMSBinding.java | 11 +- .../iris/core/nms/v1X/NMSBinding1X.java | 20 +- .../iris/core/project/SchemaBuilder.java | 19 ++ .../volmit/iris/core/service/DolphinSVC.java | 93 ------- .../volmit/iris/core/service/VillageSVC.java | 127 --------- .../volmit/iris/engine/IrisWorldManager.java | 20 ++ .../iris/engine/framework/ListFunction.java | 8 + .../iris/engine/framework/ResultLocator.java | 121 +++++++++ .../iris/engine/object/IrisDimension.java | 6 +- .../engine/object/IrisJigsawStructure.java | 4 + .../annotations/RegistryListFunction.java | 17 ++ .../annotations/StructureKeyFunction.java | 23 ++ .../volmit/iris/util/mantle/MantleFlag.java | 3 +- .../core/nms/v1_19_R1/IrisChunkGenerator.java | 236 +++++++++++++++++ .../iris/core/nms/v1_19_R1/NMSBinding.java | 39 +-- .../core/nms/v1_19_R2/IrisChunkGenerator.java | 241 +++++++++++++++++ .../iris/core/nms/v1_19_R2/NMSBinding.java | 39 +-- .../core/nms/v1_19_R3/IrisChunkGenerator.java | 241 +++++++++++++++++ .../iris/core/nms/v1_19_R3/NMSBinding.java | 33 ++- .../core/nms/v1_20_R1/IrisChunkGenerator.java | 241 +++++++++++++++++ .../iris/core/nms/v1_20_R1/NMSBinding.java | 39 +-- .../core/nms/v1_20_R2/IrisChunkGenerator.java | 241 +++++++++++++++++ .../iris/core/nms/v1_20_R2/NMSBinding.java | 39 +-- .../core/nms/v1_20_R3/IrisChunkGenerator.java | 244 ++++++++++++++++++ .../iris/core/nms/v1_20_R3/NMSBinding.java | 39 +-- .../core/nms/v1_20_R4/IrisChunkGenerator.java | 239 +++++++++++++++++ .../iris/core/nms/v1_20_R4/NMSBinding.java | 44 ++-- .../core/nms/v1_21_R1/IrisChunkGenerator.java | 240 +++++++++++++++++ .../iris/core/nms/v1_21_R1/NMSBinding.java | 40 +-- 29 files changed, 2337 insertions(+), 370 deletions(-) delete mode 100644 core/src/main/java/com/volmit/iris/core/service/DolphinSVC.java delete mode 100644 core/src/main/java/com/volmit/iris/core/service/VillageSVC.java create mode 100644 core/src/main/java/com/volmit/iris/engine/framework/ListFunction.java create mode 100644 core/src/main/java/com/volmit/iris/engine/framework/ResultLocator.java create mode 100644 core/src/main/java/com/volmit/iris/engine/object/annotations/RegistryListFunction.java create mode 100644 core/src/main/java/com/volmit/iris/engine/object/annotations/StructureKeyFunction.java create mode 100644 nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/IrisChunkGenerator.java create mode 100644 nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/IrisChunkGenerator.java create mode 100644 nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/IrisChunkGenerator.java create mode 100644 nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/IrisChunkGenerator.java create mode 100644 nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/IrisChunkGenerator.java create mode 100644 nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/IrisChunkGenerator.java create mode 100644 nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/IrisChunkGenerator.java create mode 100644 nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/IrisChunkGenerator.java 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 b2441dfd6..75c817a81 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 @@ -28,19 +28,18 @@ import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.nbt.mca.palette.MCABiomeContainer; import com.volmit.iris.util.nbt.mca.palette.MCAPaletteAccess; import com.volmit.iris.util.nbt.tag.CompoundTag; -import org.bukkit.Chunk; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.WorldCreator; +import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.entity.Dolphin; 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; import java.awt.*; +import java.awt.Color; public interface INMSBinding { boolean hasTile(Location l); @@ -107,8 +106,6 @@ public interface INMSBinding { ItemStack applyCustomNbt(ItemStack itemStack, KMap customNbt) throws IllegalArgumentException; - void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos); - void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException; Vector3d getBoundingbox(org.bukkit.entity.EntityType entity); @@ -124,4 +121,6 @@ public interface INMSBinding { default int getSpawnChunkCount(World world) { return 441; } + + KList getStructureKeys(); } 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 8ce6caba8..941cef9af 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 @@ -30,18 +30,18 @@ import com.volmit.iris.util.math.Vector3d; import com.volmit.iris.util.nbt.mca.palette.MCABiomeContainer; import com.volmit.iris.util.nbt.mca.palette.MCAPaletteAccess; import com.volmit.iris.util.nbt.tag.CompoundTag; -import org.bukkit.Chunk; -import org.bukkit.Location; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.entity.Dolphin; 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; import java.awt.*; +import java.awt.Color; public class NMSBinding1X implements INMSBinding { private static final boolean supportsCustomHeight = testCustomHeight(); @@ -81,11 +81,6 @@ public class NMSBinding1X implements INMSBinding { return itemStack; } - @Override - public void setTreasurePos(Dolphin dolphin, BlockPos pos) { - - } - @Override public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { @@ -105,6 +100,15 @@ public class NMSBinding1X implements INMSBinding { return Color.GREEN; } + @Override + public KList getStructureKeys() { + var list = Registry.STRUCTURE.stream() + .map(Structure::getKey) + .map(NamespacedKey::toString) + .toList(); + return new KList<>(list); + } + @Override public void deserializeTile(CompoundTag s, Location newPosition) { diff --git a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java index 7cdf4dd76..ea672a78f 100644 --- a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java +++ b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java @@ -22,6 +22,7 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisRegistrant; import com.volmit.iris.core.loader.ResourceLoader; +import com.volmit.iris.engine.framework.ListFunction; import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; @@ -309,6 +310,24 @@ public class SchemaBuilder { fancyType = "Enchantment Type"; prop.put("$ref", "#/definitions/" + key); description.add(SYMBOL_TYPE__N + " Must be a valid Enchantment Type (use ctrl+space for auto complete!)"); + } else if (k.isAnnotationPresent(RegistryListFunction.class)) { + var functionClass = k.getDeclaredAnnotation(RegistryListFunction.class).value(); + try { + var instance = functionClass.getDeclaredConstructor().newInstance(); + String key = instance.key(); + fancyType = instance.fancyName(); + + if (!definitions.containsKey(key)) { + JSONObject j = new JSONObject(); + j.put("enum", instance.apply(data)); + definitions.put(key, j); + } + + prop.put("$ref", "#/definitions/" + key); + description.add(SYMBOL_TYPE__N + " Must be a valid " + fancyType + " (use ctrl+space for auto complete!)"); + } catch (Throwable e) { + Iris.error("Could not execute apply method in " + functionClass.getName()); + } } else if (k.getType().equals(PotionEffectType.class)) { String key = "enum-potion-effect-type"; diff --git a/core/src/main/java/com/volmit/iris/core/service/DolphinSVC.java b/core/src/main/java/com/volmit/iris/core/service/DolphinSVC.java deleted file mode 100644 index 0f4aa53dc..000000000 --- a/core/src/main/java/com/volmit/iris/core/service/DolphinSVC.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2022 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.volmit.iris.core.service; - -import com.volmit.iris.core.nms.INMS; -import com.volmit.iris.core.nms.container.BlockPos; -import com.volmit.iris.core.tools.IrisToolbelt; -import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.util.documentation.ChunkCoordinates; -import com.volmit.iris.util.function.Consumer4; -import com.volmit.iris.util.math.Spiraler; -import com.volmit.iris.util.matter.MatterStructurePOI; -import com.volmit.iris.util.plugin.IrisService; -import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.SoundCategory; -import org.bukkit.entity.Dolphin; -import org.bukkit.entity.EntityType; -import org.bukkit.event.EventHandler; -import org.bukkit.event.player.PlayerInteractEntityEvent; -import org.bukkit.generator.structure.StructureType; - -import java.util.concurrent.atomic.AtomicReference; - -public class DolphinSVC implements IrisService { - - @Override - public void onEnable() { - - } - - @Override - public void onDisable() { - - } - - @EventHandler - public void on(PlayerInteractEntityEvent event) { - if (!IrisToolbelt.isIrisWorld(event.getPlayer().getWorld())) { - return; - } - - Material hand = event.getPlayer().getInventory().getItem(event.getHand()).getType(); - if (event.getRightClicked().getType().equals(EntityType.DOLPHIN) && (hand.equals(Material.TROPICAL_FISH) || hand.equals(Material.PUFFERFISH) || hand.equals(Material.COD) || hand.equals(Material.SALMON))) { - Engine e = IrisToolbelt.access(event.getPlayer().getWorld()).getEngine(); - searchNearestTreasure(e, event.getPlayer().getLocation().getBlockX() >> 4, event.getPlayer().getLocation().getBlockZ() >> 4, e.getMantle().getRadius() - 1, StructureType.BURIED_TREASURE, (x, y, z, p) -> { - event.setCancelled(true); - Dolphin d = (Dolphin) event.getRightClicked(); - INMS.get().setTreasurePos(d, new BlockPos(x, y, z)); - d.getWorld().playSound(d, Sound.ENTITY_DOLPHIN_EAT, SoundCategory.NEUTRAL, 1, 1); - }); - - } - } - - @ChunkCoordinates - public void findTreasure(Engine engine, int chunkX, int chunkY, StructureType type, Consumer4 consumer) { - AtomicReference ref = new AtomicReference<>(); - engine.getMantle().getMantle().iterateChunk(chunkX, chunkY, MatterStructurePOI.class, ref.get() == null ? (x, y, z, d) -> { - if (d.getType().equals(type.getKey().getKey())) { - ref.set(d); - consumer.accept(x, y, z, d); - } - } : (x, y, z, d) -> { - }); - } - - @ChunkCoordinates - public void searchNearestTreasure(Engine engine, int chunkX, int chunkY, int radius, StructureType type, Consumer4 consumer) { - AtomicReference ref = new AtomicReference<>(); - new Spiraler(radius * 2, radius * 2, (x, z) -> findTreasure(engine, x, z, type, ref.get() == null ? (i, d, g, a) -> { - ref.set(a); - consumer.accept(i, d, g, a); - } : (i, d, g, a) -> { - })).setOffset(chunkX, chunkY).drain(); - } -} diff --git a/core/src/main/java/com/volmit/iris/core/service/VillageSVC.java b/core/src/main/java/com/volmit/iris/core/service/VillageSVC.java deleted file mode 100644 index 820f40833..000000000 --- a/core/src/main/java/com/volmit/iris/core/service/VillageSVC.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2022 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.volmit.iris.core.service; - -import com.volmit.iris.Iris; -import com.volmit.iris.core.IrisSettings; -import com.volmit.iris.core.tools.IrisToolbelt; -import com.volmit.iris.engine.object.IrisDimension; -import com.volmit.iris.util.format.C; -import com.volmit.iris.util.plugin.IrisService; -import com.volmit.iris.util.plugin.VolmitSender; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.entity.Villager; -import org.bukkit.event.EventHandler; -import org.bukkit.event.entity.VillagerCareerChangeEvent; - -import java.util.List; - -public class VillageSVC implements IrisService { - @Override - public void onEnable() { - - } - - @Override - public void onDisable() { - - } - - @EventHandler - public void on(VillagerCareerChangeEvent event) { - - if (!IrisToolbelt.isIrisWorld(event.getEntity().getWorld())) { - return; - } - - IrisDimension dim = IrisToolbelt.access(event.getEntity().getWorld()) - .getEngine().getDimension(); - - if (!dim.isRemoveCartographersDueToCrash()) { - return; - } - - if (event.getProfession().equals(Villager.Profession.CARTOGRAPHER)) { - event.setCancelled(true); - - Location eventLocation = event.getEntity().getLocation(); - - int radius = dim.getNotifyPlayersOfCartographerCancelledRadius(); - - if (radius == -1) { - return; - } - - List playersInWorld = event.getEntity().getWorld().getPlayers(); - - String message = C.GOLD + IrisSettings.get().getGeneral().cartographerMessage; - - Iris.info("Cancelled Cartographer Villager to prevent server crash at " + eventLocation + "!"); - - if (radius == -2) { - playersInWorld.stream().map(VolmitSender::new).forEach(v -> v.sendMessage(message)); - } else { - playersInWorld.forEach(p -> { - if (p.getLocation().distance(eventLocation) < radius) { - new VolmitSender(p).sendMessage(message); - } - }); - } - - } - } - - /* - * Replace or disable villager trade add event to prevent explorer map - */ - /* Removed due to MC breaking stuff again. This event is now called after the cartographer maps are made, - so it can fuck right off. - @EventHandler - public void on(VillagerAcquireTradeEvent event) { - if(!IrisToolbelt.isIrisWorld((event.getEntity().getWorld()))) { - return; - } - - // Iris.info("Trade event: type " + event.getRecipe().getResult().getType() + " / meta " + event.getRecipe().getResult().getItemMeta() + " / data " + event.getRecipe().getResult().getData()); - if(!event.getRecipe().getResult().getType().equals(Material.FILLED_MAP)) { - return; - } - - IrisVillagerOverride override = IrisToolbelt.access(event.getEntity().getWorld()).getEngine() - .getDimension().getPatchCartographers(); - - if(override.isDisableTrade()) { - event.setCancelled(true); - Iris.debug("Cancelled cartographer trade @ " + event.getEntity().getLocation()); - return; - } - - if(override.getValidItems() == null) { - event.setCancelled(true); - Iris.debug("Cancelled cartographer trade because no override items are valid @ " + event.getEntity().getLocation()); - return; - } - - IrisVillagerTrade trade = override.getValidItems().getRandom(); - event.setRecipe(trade.convert()); - Iris.debug("Overrode cartographer trade with: " + trade + " to prevent allowing cartography map trades"); - } - */ -} diff --git a/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java b/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java index cfaa4383a..075e8660d 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisWorldManager.java @@ -76,6 +76,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager { private final ChronoLatch ecl; private final ChronoLatch cln; private final ChronoLatch chunkUpdater; + private final ChronoLatch chunkDiscovery; private double energy = 25; private int entityCount = 0; private long charge = 0; @@ -92,12 +93,14 @@ public class IrisWorldManager extends EngineAssignedWorldManager { clw = null; looper = null; chunkUpdater = null; + chunkDiscovery = null; id = -1; } public IrisWorldManager(Engine engine) { super(engine); chunkUpdater = new ChronoLatch(3000); + chunkDiscovery = new ChronoLatch(5000); cln = new ChronoLatch(60000); cl = new ChronoLatch(3000); ecl = new ChronoLatch(250); @@ -128,6 +131,10 @@ public class IrisWorldManager extends EngineAssignedWorldManager { updateChunks(); } + if (chunkDiscovery.flip()) { + discoverChunks(); + } + if (getDimension().isInfiniteEnergy()) { energy += 1000; @@ -174,6 +181,19 @@ public class IrisWorldManager extends EngineAssignedWorldManager { looper.start(); } + private void discoverChunks() { + var mantle = getEngine().getMantle().getMantle(); + for (Player i : getEngine().getWorld().realWorld().getPlayers()) { + int r = 1; + + for (int x = -r; x <= r; x++) { + for (int z = -r; z <= r; z++) { + mantle.getChunk(i.getLocation().getChunk()).flag(MantleFlag.DISCOVERED, true); + } + } + } + } + private void updateChunks() { for (Player i : getEngine().getWorld().realWorld().getPlayers()) { int r = 1; diff --git a/core/src/main/java/com/volmit/iris/engine/framework/ListFunction.java b/core/src/main/java/com/volmit/iris/engine/framework/ListFunction.java new file mode 100644 index 000000000..2d2c8bfdb --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/framework/ListFunction.java @@ -0,0 +1,8 @@ +package com.volmit.iris.engine.framework; + +import java.util.function.Function; + +public interface ListFunction extends Function { + String key(); + String fancyName(); +} diff --git a/core/src/main/java/com/volmit/iris/engine/framework/ResultLocator.java b/core/src/main/java/com/volmit/iris/engine/framework/ResultLocator.java new file mode 100644 index 000000000..93d698347 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/framework/ResultLocator.java @@ -0,0 +1,121 @@ +package com.volmit.iris.engine.framework; + +import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.engine.object.IrisJigsawStructure; +import com.volmit.iris.engine.object.IrisObject; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.math.Position2; +import com.volmit.iris.util.math.Spiraler; +import com.volmit.iris.util.parallel.BurstExecutor; +import com.volmit.iris.util.parallel.MultiBurst; +import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import org.apache.commons.lang3.function.TriFunction; + +import java.util.Collection; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +@FunctionalInterface +public interface ResultLocator { + static void cancelSearch() { + if (LocatorCanceller.cancel != null) { + LocatorCanceller.cancel.run(); + LocatorCanceller.cancel = null; + } + } + + static ResultLocator locateStructure(Collection keys) { + return (e, pos) -> { + var structure = e.getStructureAt(pos.getX(), pos.getZ()); + return structure != null && keys.contains(structure.getLoadKey()) ? structure : null; + }; + } + + static ResultLocator locateObject(Collection keys) { + return (e, pos) -> { + Set objects = e.getObjectsAt(pos.getX(), pos.getZ()); + for (String object : objects) { + if (!keys.contains(object)) continue; + return e.getData().getObjectLoader().load(object); + } + return null; + }; + } + + T find(Engine e, Position2 chunkPos); + + default ResultLocator then(TriFunction filter) { + return (e, pos) -> { + var t = find(e, pos); + return t != null ? filter.apply(e, pos, t) : null; + }; + } + + default Future> find(Engine engine, Position2 pos, long timeout, Consumer checks, boolean cancelable) throws WrongEngineBroException { + if (engine.isClosed()) { + throw new WrongEngineBroException(); + } + + cancelSearch(); + + return MultiBurst.burst.completeValue(() -> { + int tc = IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism()) * 17; + MultiBurst burst = MultiBurst.burst; + AtomicBoolean found = new AtomicBoolean(false); + AtomicReference> foundObj = new AtomicReference<>(); + Position2 cursor = pos; + AtomicInteger searched = new AtomicInteger(); + AtomicBoolean stop = new AtomicBoolean(false); + PrecisionStopwatch px = PrecisionStopwatch.start(); + if (cancelable) LocatorCanceller.cancel = () -> stop.set(true); + AtomicReference next = new AtomicReference<>(cursor); + Spiraler s = new Spiraler(100000, 100000, (x, z) -> next.set(new Position2(x, z))); + s.setOffset(cursor.getX(), cursor.getZ()); + s.next(); + while (!found.get() && !stop.get() && px.getMilliseconds() < timeout) { + BurstExecutor e = burst.burst(tc); + + for (int i = 0; i < tc; i++) { + Position2 p = next.get(); + s.next(); + e.queue(() -> { + var o = find(engine, p); + if (o != null) { + if (foundObj.get() == null) { + foundObj.set(new Result<>(o, p)); + } + + found.set(true); + } + searched.incrementAndGet(); + }); + } + + e.complete(); + checks.accept(searched.get()); + } + + LocatorCanceller.cancel = null; + + if (found.get() && foundObj.get() != null) { + return foundObj.get(); + } + + return null; + }); + } + + record Result(T obj, Position2 pos) { + public int getBlockX() { + return (pos.getX() << 4) + 8; + } + + public int getBlockZ() { + return (pos.getZ() << 4) + 8; + } + } +} diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java index 7206b2a3d..fae6ee23b 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDimension.java @@ -300,10 +300,8 @@ public class IrisDimension extends IrisRegistrant { private IrisMaterialPalette rockPalette = new IrisMaterialPalette().qclear().qadd("stone"); @Desc("The palette of blocks for 'water'") private IrisMaterialPalette fluidPalette = new IrisMaterialPalette().qclear().qadd("water"); - @Desc("Remove cartographers so they do not crash the server (Iris worlds only)") - private boolean removeCartographersDueToCrash = true; - @Desc("Notify players of cancelled cartographer villager in this radius in blocks (set to -1 to disable, -2 for everyone)") - private int notifyPlayersOfCartographerCancelledRadius = 30; + @Desc("Prevent cartographers to generate explorer maps (Iris worlds only)\nONLY TOUCH IF YOUR SERVER CRASHES WHILE GENERATING EXPLORER MAPS") + private boolean disableExplorerMaps = false; @Desc("Collection of ores to be generated") @ArrayType(type = IrisOreGenerator.class, min = 1) private KList ores = new KList<>(); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructure.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructure.java index 9d6d28506..d4a577101 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructure.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawStructure.java @@ -69,6 +69,10 @@ public class IrisJigsawStructure extends IrisRegistrant { @Desc("Set to true to prevent rotating the initial structure piece") private boolean disableInitialRotation = false; + @RegistryListFunction(StructureKeyFunction.class) + @Desc("The minecraft key to use when creating treasure maps") + private String structureKey = null; + private transient AtomicCache maxDimension = new AtomicCache<>(); private void loadPool(String p, KList pools, KList pieces) { diff --git a/core/src/main/java/com/volmit/iris/engine/object/annotations/RegistryListFunction.java b/core/src/main/java/com/volmit/iris/engine/object/annotations/RegistryListFunction.java new file mode 100644 index 000000000..e437ebfae --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/object/annotations/RegistryListFunction.java @@ -0,0 +1,17 @@ +package com.volmit.iris.engine.object.annotations; + +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.engine.framework.ListFunction; +import com.volmit.iris.util.collection.KList; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention(RUNTIME) +@Target({PARAMETER, TYPE, FIELD}) +public @interface RegistryListFunction { + Class>> value(); +} diff --git a/core/src/main/java/com/volmit/iris/engine/object/annotations/StructureKeyFunction.java b/core/src/main/java/com/volmit/iris/engine/object/annotations/StructureKeyFunction.java new file mode 100644 index 000000000..5ab656cc8 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/engine/object/annotations/StructureKeyFunction.java @@ -0,0 +1,23 @@ +package com.volmit.iris.engine.object.annotations; + +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.nms.INMS; +import com.volmit.iris.engine.framework.ListFunction; +import com.volmit.iris.util.collection.KList; + +public class StructureKeyFunction implements ListFunction> { + @Override + public String key() { + return "structure-key"; + } + + @Override + public String fancyName() { + return "Structure Key"; + } + + @Override + public KList apply(IrisData irisData) { + return INMS.get().getStructureKeys(); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/MantleFlag.java b/core/src/main/java/com/volmit/iris/util/mantle/MantleFlag.java index 6bb5f1e6f..fc2b9762f 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/MantleFlag.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/MantleFlag.java @@ -34,7 +34,8 @@ public enum MantleFlag { PLANNED, ETCHED, TILE, - CUSTOM; + CUSTOM, + DISCOVERED; static StateList getStateList() { return new StateList(MantleFlag.values()); diff --git a/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/IrisChunkGenerator.java b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/IrisChunkGenerator.java new file mode 100644 index 000000000..040d588df --- /dev/null +++ b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/IrisChunkGenerator.java @@ -0,0 +1,236 @@ +package com.volmit.iris.core.nms.v1_19_R1; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.ResultLocator; +import com.volmit.iris.engine.framework.WrongEngineBroException; +import com.volmit.iris.engine.object.IrisJigsawStructure; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.math.Position2; +import net.minecraft.core.*; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.TagKey; +import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.NoiseColumn; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R1.generator.CustomChunkGenerator; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +public class IrisChunkGenerator extends CustomChunkGenerator { + private final ChunkGenerator delegate; + private final Engine engine; + private final BiomeSource biomeSource; + private final KMap, KSet> structures = new KMap<>(); + + public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { + super(((CraftWorld) world).getHandle(), delegate, null); + this.delegate = delegate; + this.engine = engine; + this.biomeSource = new CustomBiomeSource(seed, engine, world); + var dimension = engine.getDimension(); + + KSet placements = new KSet<>(); + addAll(dimension.getJigsawStructures(), placements); + for (var region : dimension.getAllRegions(engine)) { + addAll(region.getJigsawStructures(), placements); + for (var biome : region.getAllBiomes(engine)) + addAll(biome.getJigsawStructures(), placements); + } + var stronghold = dimension.getStronghold(); + if (stronghold != null) + placements.add(engine.getData().getJigsawStructureLoader().load(stronghold)); + placements.removeIf(Objects::isNull); + + var registry = ((CraftWorld) world).getHandle().registryAccess().registry(Registry.STRUCTURE_REGISTRY).orElseThrow(); + for (var s : placements) { + try { + String raw = s.getStructureKey(); + if (raw == null) continue; + boolean tag = raw.startsWith("#"); + if (tag) raw = raw.substring(1); + + var location = new ResourceLocation(raw); + if (!tag) { + structures.computeIfAbsent(ResourceKey.create(Registry.STRUCTURE_REGISTRY, location), k -> new KSet<>()).add(s.getLoadKey()); + continue; + } + + var key = TagKey.create(Registry.STRUCTURE_REGISTRY, location); + var set = registry.getTag(key).orElse(null); + if (set == null) { + Iris.error("Could not find structure tag: " + raw); + continue; + } + for (var holder : set) { + var resourceKey = holder.unwrapKey().orElse(null); + if (resourceKey == null) continue; + structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey()); + } + } catch (Throwable e) { + Iris.error("Failed to load structure: " + s.getLoadKey()); + e.printStackTrace(); + } + } + } + + private void addAll(KList placements, KSet structures) { + if (placements == null) return; + placements.stream() + .map(IrisJigsawStructurePlacement::getStructure) + .map(engine.getData().getJigsawStructureLoader()::load) + .filter(Objects::nonNull) + .forEach(structures::add); + } + + @Override + public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (engine.getDimension().isDisableExplorerMaps()) + return null; + + KMap> structures = new KMap<>(); + for (var holder : holders) { + if (holder == null) continue; + var key = holder.unwrapKey().orElse(null); + var set = this.structures.get(key); + if (set == null) continue; + for (var structure : set) { + structures.put(structure, holder); + } + } + if (structures.isEmpty()) + return null; + + var locator = ResultLocator.locateStructure(structures.keySet()) + .then((e, p , s) -> structures.get(s.getLoadKey())); + if (findUnexplored) + locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s); + + try { + var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get(); + if (result == null) return null; + var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ()); + return Pair.of(blockPos, result.obj()); + } catch (WrongEngineBroException | ExecutionException | InterruptedException e) { + return null; + } + } + + @Override + protected Codec codec() { + return Codec.unit(null); + } + + @Override + public ChunkGenerator getDelegate() { + if (delegate instanceof CustomChunkGenerator chunkGenerator) + return chunkGenerator.getDelegate(); + return delegate; + } + + @Override + public BiomeSource getBiomeSource() { + return biomeSource; + } + + @Override + public int getMinY() { + return delegate.getMinY(); + } + + @Override + public int getSeaLevel() { + return delegate.getSeaLevel(); + } + + @Override + public void createStructures(RegistryAccess iregistrycustom, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, long i) { + delegate.createStructures(iregistrycustom, randomstate, structuremanager, ichunkaccess, structuretemplatemanager, i); + } + + @Override + public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) { + delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess); + } + + @Override + public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess, GenerationStep.Carving worldgenstage_features) { + delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess, worldgenstage_features); + } + + @Override + public CompletableFuture fillFromNoise(Executor executor, Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) { + return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess); + } + + @Override + public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public WeightedRandomList getMobsAt(Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { + return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition); + } + + @Override + public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager); + } + + @Override + public void addDebugScreenInfo(List list, RandomState randomstate, BlockPos blockposition) { + delegate.addDebugScreenInfo(list, randomstate, blockposition); + } + + @Override + public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) { + delegate.spawnOriginalMobs(regionlimitedworldaccess); + } + + @Override + public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) { + return delegate.getSpawnHeight(levelheightaccessor); + } + + @Override + public int getGenDepth() { + return delegate.getGenDepth(); + } + + @Override + public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate); + } +} 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 058e4046d..4028d8102 100644 --- a/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java +++ b/nms/v1_19_R1/src/main/java/com/volmit/iris/core/nms/v1_19_R1/NMSBinding.java @@ -13,7 +13,9 @@ import java.util.List; import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; +import com.mojang.datafixers.util.Pair; import com.volmit.iris.core.nms.container.BiomeColor; +import net.minecraft.tags.TagKey; import net.minecraft.world.level.LevelReader; import org.bukkit.*; import org.bukkit.block.Biome; @@ -473,23 +475,9 @@ public class NMSBinding implements INMSBinding { } } - public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) { - CraftDolphin cd = (CraftDolphin)dolphin; - cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ())); - cd.getHandle().setGotFish(true); - } - - public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { - ServerLevel serverLevel = ((CraftWorld)world).getHandle(); - Class clazz = serverLevel.getChunkSource().chunkMap.generator.getClass(); - Field biomeSource = getField(clazz, BiomeSource.class); - biomeSource.setAccessible(true); - Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - unsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe)unsafeField.get(null); - CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world); - unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource); - biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); + public void inject(long seed, Engine engine, World world) { + var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap; + chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world); } public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { @@ -544,6 +532,23 @@ public class NMSBinding implements INMSBinding { return new Color(rgba, true); } + @Override + public KList getStructureKeys() { + KList keys = new KList<>(); + + var registry = registry().registry(Registry.STRUCTURE_REGISTRY).orElse(null); + if (registry == null) return keys; + registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add); + registry.getTags() + .map(Pair::getFirst) + .map(TagKey::location) + .map(ResourceLocation::toString) + .map(s -> "#" + s) + .forEach(keys::add); + + return keys; + } + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { try { for (Field f : clazz.getDeclaredFields()) { diff --git a/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/IrisChunkGenerator.java b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/IrisChunkGenerator.java new file mode 100644 index 000000000..9bb31ef8c --- /dev/null +++ b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/IrisChunkGenerator.java @@ -0,0 +1,241 @@ +package com.volmit.iris.core.nms.v1_19_R2; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.ResultLocator; +import com.volmit.iris.engine.framework.WrongEngineBroException; +import com.volmit.iris.engine.object.IrisJigsawStructure; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.math.Position2; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.TagKey; +import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.NoiseColumn; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_19_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R2.generator.CustomChunkGenerator; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +public class IrisChunkGenerator extends CustomChunkGenerator { + private final ChunkGenerator delegate; + private final Engine engine; + private final BiomeSource biomeSource; + private final KMap, KSet> structures = new KMap<>(); + + public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { + super(((CraftWorld) world).getHandle(), delegate, null); + this.delegate = delegate; + this.engine = engine; + this.biomeSource = new CustomBiomeSource(seed, engine, world); + var dimension = engine.getDimension(); + + KSet placements = new KSet<>(); + addAll(dimension.getJigsawStructures(), placements); + for (var region : dimension.getAllRegions(engine)) { + addAll(region.getJigsawStructures(), placements); + for (var biome : region.getAllBiomes(engine)) + addAll(biome.getJigsawStructures(), placements); + } + var stronghold = dimension.getStronghold(); + if (stronghold != null) + placements.add(engine.getData().getJigsawStructureLoader().load(stronghold)); + placements.removeIf(Objects::isNull); + + var registry = ((CraftWorld) world).getHandle().registryAccess().registry(Registries.STRUCTURE).orElseThrow(); + for (var s : placements) { + try { + String raw = s.getStructureKey(); + if (raw == null) continue; + boolean tag = raw.startsWith("#"); + if (tag) raw = raw.substring(1); + + var location = new ResourceLocation(raw); + if (!tag) { + structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey()); + continue; + } + + var key = TagKey.create(Registries.STRUCTURE, location); + var set = registry.getTag(key).orElse(null); + if (set == null) { + Iris.error("Could not find structure tag: " + raw); + continue; + } + for (var holder : set) { + var resourceKey = holder.unwrapKey().orElse(null); + if (resourceKey == null) continue; + structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey()); + } + } catch (Throwable e) { + Iris.error("Failed to load structure: " + s.getLoadKey()); + e.printStackTrace(); + } + } + } + + private void addAll(KList placements, KSet structures) { + if (placements == null) return; + placements.stream() + .map(IrisJigsawStructurePlacement::getStructure) + .map(engine.getData().getJigsawStructureLoader()::load) + .filter(Objects::nonNull) + .forEach(structures::add); + } + + @Override + public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (engine.getDimension().isDisableExplorerMaps()) + return null; + + KMap> structures = new KMap<>(); + for (var holder : holders) { + if (holder == null) continue; + var key = holder.unwrapKey().orElse(null); + var set = this.structures.get(key); + if (set == null) continue; + for (var structure : set) { + structures.put(structure, holder); + } + } + if (structures.isEmpty()) + return null; + + var locator = ResultLocator.locateStructure(structures.keySet()) + .then((e, p , s) -> structures.get(s.getLoadKey())); + if (findUnexplored) + locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s); + + try { + var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get(); + if (result == null) return null; + var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ()); + return Pair.of(blockPos, result.obj()); + } catch (WrongEngineBroException | ExecutionException | InterruptedException e) { + return null; + } + } + + @Override + protected Codec codec() { + return Codec.unit(null); + } + + @Override + public ChunkGenerator getDelegate() { + if (delegate instanceof CustomChunkGenerator chunkGenerator) + return chunkGenerator.getDelegate(); + return delegate; + } + + @Override + public BiomeSource getBiomeSource() { + return biomeSource; + } + + @Override + public int getMinY() { + return delegate.getMinY(); + } + + @Override + public int getSeaLevel() { + return delegate.getSeaLevel(); + } + + @Override + public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) { + delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager); + } + + @Override + public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) { + delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess); + } + + @Override + public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess, GenerationStep.Carving worldgenstage_features) { + delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess, worldgenstage_features); + } + + @Override + public CompletableFuture fillFromNoise(Executor executor, Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) { + return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess); + } + + @Override + public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public WeightedRandomList getMobsAt(Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { + return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition); + } + + @Override + public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager); + } + + @Override + public void addDebugScreenInfo(List list, RandomState randomstate, BlockPos blockposition) { + delegate.addDebugScreenInfo(list, randomstate, blockposition); + } + + @Override + public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) { + delegate.spawnOriginalMobs(regionlimitedworldaccess); + } + + @Override + public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) { + return delegate.getSpawnHeight(levelheightaccessor); + } + + @Override + public int getGenDepth() { + return delegate.getGenDepth(); + } + + @Override + public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate); + } +} 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 f5f778002..75ab7732e 100644 --- a/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java +++ b/nms/v1_19_R2/src/main/java/com/volmit/iris/core/nms/v1_19_R2/NMSBinding.java @@ -13,7 +13,9 @@ import java.util.List; import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; +import com.mojang.datafixers.util.Pair; import com.volmit.iris.core.nms.container.BiomeColor; +import net.minecraft.tags.TagKey; import net.minecraft.world.level.LevelReader; import org.bukkit.*; import org.bukkit.block.Biome; @@ -474,23 +476,9 @@ public class NMSBinding implements INMSBinding { } } - public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) { - CraftDolphin cd = (CraftDolphin)dolphin; - cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ())); - cd.getHandle().setGotFish(true); - } - - public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { - ServerLevel serverLevel = ((CraftWorld)world).getHandle(); - Class clazz = serverLevel.getChunkSource().chunkMap.generator.getClass(); - Field biomeSource = getField(clazz, BiomeSource.class); - biomeSource.setAccessible(true); - Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - unsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe)unsafeField.get(null); - CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world); - unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource); - biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); + public void inject(long seed, Engine engine, World world) { + var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap; + chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world); } @@ -546,6 +534,23 @@ public class NMSBinding implements INMSBinding { return new Color(rgba, true); } + @Override + public KList getStructureKeys() { + KList keys = new KList<>(); + + var registry = registry().registry(Registries.STRUCTURE).orElse(null); + if (registry == null) return keys; + registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add); + registry.getTags() + .map(Pair::getFirst) + .map(TagKey::location) + .map(ResourceLocation::toString) + .map(s -> "#" + s) + .forEach(keys::add); + + return keys; + } + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { try { for (Field f : clazz.getDeclaredFields()) { diff --git a/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/IrisChunkGenerator.java b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/IrisChunkGenerator.java new file mode 100644 index 000000000..358ef9829 --- /dev/null +++ b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/IrisChunkGenerator.java @@ -0,0 +1,241 @@ +package com.volmit.iris.core.nms.v1_19_R3; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.ResultLocator; +import com.volmit.iris.engine.framework.WrongEngineBroException; +import com.volmit.iris.engine.object.IrisJigsawStructure; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.math.Position2; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.TagKey; +import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.NoiseColumn; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R3.generator.CustomChunkGenerator; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +public class IrisChunkGenerator extends CustomChunkGenerator { + private final ChunkGenerator delegate; + private final Engine engine; + private final BiomeSource biomeSource; + private final KMap, KSet> structures = new KMap<>(); + + public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { + super(((CraftWorld) world).getHandle(), delegate, null); + this.delegate = delegate; + this.engine = engine; + this.biomeSource = new CustomBiomeSource(seed, engine, world); + var dimension = engine.getDimension(); + + KSet placements = new KSet<>(); + addAll(dimension.getJigsawStructures(), placements); + for (var region : dimension.getAllRegions(engine)) { + addAll(region.getJigsawStructures(), placements); + for (var biome : region.getAllBiomes(engine)) + addAll(biome.getJigsawStructures(), placements); + } + var stronghold = dimension.getStronghold(); + if (stronghold != null) + placements.add(engine.getData().getJigsawStructureLoader().load(stronghold)); + placements.removeIf(Objects::isNull); + + var registry = ((CraftWorld) world).getHandle().registryAccess().registry(Registries.STRUCTURE).orElseThrow(); + for (var s : placements) { + try { + String raw = s.getStructureKey(); + if (raw == null) continue; + boolean tag = raw.startsWith("#"); + if (tag) raw = raw.substring(1); + + var location = new ResourceLocation(raw); + if (!tag) { + structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey()); + continue; + } + + var key = TagKey.create(Registries.STRUCTURE, location); + var set = registry.getTag(key).orElse(null); + if (set == null) { + Iris.error("Could not find structure tag: " + raw); + continue; + } + for (var holder : set) { + var resourceKey = holder.unwrapKey().orElse(null); + if (resourceKey == null) continue; + structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey()); + } + } catch (Throwable e) { + Iris.error("Failed to load structure: " + s.getLoadKey()); + e.printStackTrace(); + } + } + } + + private void addAll(KList placements, KSet structures) { + if (placements == null) return; + placements.stream() + .map(IrisJigsawStructurePlacement::getStructure) + .map(engine.getData().getJigsawStructureLoader()::load) + .filter(Objects::nonNull) + .forEach(structures::add); + } + + @Override + public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (engine.getDimension().isDisableExplorerMaps()) + return null; + + KMap> structures = new KMap<>(); + for (var holder : holders) { + if (holder == null) continue; + var key = holder.unwrapKey().orElse(null); + var set = this.structures.get(key); + if (set == null) continue; + for (var structure : set) { + structures.put(structure, holder); + } + } + if (structures.isEmpty()) + return null; + + var locator = ResultLocator.locateStructure(structures.keySet()) + .then((e, p , s) -> structures.get(s.getLoadKey())); + if (findUnexplored) + locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s); + + try { + var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get(); + if (result == null) return null; + var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ()); + return Pair.of(blockPos, result.obj()); + } catch (WrongEngineBroException | ExecutionException | InterruptedException e) { + return null; + } + } + + @Override + protected Codec codec() { + return Codec.unit(null); + } + + @Override + public ChunkGenerator getDelegate() { + if (delegate instanceof CustomChunkGenerator chunkGenerator) + return chunkGenerator.getDelegate(); + return delegate; + } + + @Override + public BiomeSource getBiomeSource() { + return biomeSource; + } + + @Override + public int getMinY() { + return delegate.getMinY(); + } + + @Override + public int getSeaLevel() { + return delegate.getSeaLevel(); + } + + @Override + public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) { + delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager); + } + + @Override + public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) { + delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess); + } + + @Override + public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess, GenerationStep.Carving worldgenstage_features) { + delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess, worldgenstage_features); + } + + @Override + public CompletableFuture fillFromNoise(Executor executor, Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) { + return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess); + } + + @Override + public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public WeightedRandomList getMobsAt(Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { + return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition); + } + + @Override + public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager); + } + + @Override + public void addDebugScreenInfo(List list, RandomState randomstate, BlockPos blockposition) { + delegate.addDebugScreenInfo(list, randomstate, blockposition); + } + + @Override + public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) { + delegate.spawnOriginalMobs(regionlimitedworldaccess); + } + + @Override + public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) { + return delegate.getSpawnHeight(levelheightaccessor); + } + + @Override + public int getGenDepth() { + return delegate.getGenDepth(); + } + + @Override + public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate); + } +} 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 0cbf158bd..5fd9e2507 100644 --- a/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java +++ b/nms/v1_19_R3/src/main/java/com/volmit/iris/core/nms/v1_19_R3/NMSBinding.java @@ -13,7 +13,9 @@ import java.util.List; import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; +import com.mojang.datafixers.util.Pair; import com.volmit.iris.core.nms.container.BiomeColor; +import net.minecraft.tags.TagKey; import net.minecraft.world.level.LevelReader; import org.bukkit.*; import org.bukkit.block.Biome; @@ -482,17 +484,9 @@ public class NMSBinding implements INMSBinding { cd.getHandle().setGotFish(true); } - public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { - ServerLevel serverLevel = ((CraftWorld)world).getHandle(); - Class clazz = serverLevel.getChunkSource().chunkMap.generator.getClass(); - Field biomeSource = getField(clazz, BiomeSource.class); - biomeSource.setAccessible(true); - Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - unsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe)unsafeField.get(null); - CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world); - unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource); - biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); + public void inject(long seed, Engine engine, World world) { + var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap; + chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world); } public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { @@ -550,6 +544,23 @@ public class NMSBinding implements INMSBinding { return new Color(rgba, true); } + @Override + public KList getStructureKeys() { + KList keys = new KList<>(); + + var registry = registry().registry(Registries.STRUCTURE).orElse(null); + if (registry == null) return keys; + registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add); + registry.getTags() + .map(Pair::getFirst) + .map(TagKey::location) + .map(ResourceLocation::toString) + .map(s -> "#" + s) + .forEach(keys::add); + + return keys; + } + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { try { for (Field f : clazz.getDeclaredFields()) { diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/IrisChunkGenerator.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/IrisChunkGenerator.java new file mode 100644 index 000000000..f415bbe69 --- /dev/null +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/IrisChunkGenerator.java @@ -0,0 +1,241 @@ +package com.volmit.iris.core.nms.v1_20_R1; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.ResultLocator; +import com.volmit.iris.engine.framework.WrongEngineBroException; +import com.volmit.iris.engine.object.IrisJigsawStructure; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.math.Position2; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.TagKey; +import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.NoiseColumn; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R1.generator.CustomChunkGenerator; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +public class IrisChunkGenerator extends CustomChunkGenerator { + private final ChunkGenerator delegate; + private final Engine engine; + private final BiomeSource biomeSource; + private final KMap, KSet> structures = new KMap<>(); + + public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { + super(((CraftWorld) world).getHandle(), delegate, null); + this.delegate = delegate; + this.engine = engine; + this.biomeSource = new CustomBiomeSource(seed, engine, world); + var dimension = engine.getDimension(); + + KSet placements = new KSet<>(); + addAll(dimension.getJigsawStructures(), placements); + for (var region : dimension.getAllRegions(engine)) { + addAll(region.getJigsawStructures(), placements); + for (var biome : region.getAllBiomes(engine)) + addAll(biome.getJigsawStructures(), placements); + } + var stronghold = dimension.getStronghold(); + if (stronghold != null) + placements.add(engine.getData().getJigsawStructureLoader().load(stronghold)); + placements.removeIf(Objects::isNull); + + var registry = ((CraftWorld) world).getHandle().registryAccess().registry(Registries.STRUCTURE).orElseThrow(); + for (var s : placements) { + try { + String raw = s.getStructureKey(); + if (raw == null) continue; + boolean tag = raw.startsWith("#"); + if (tag) raw = raw.substring(1); + + var location = new ResourceLocation(raw); + if (!tag) { + structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey()); + continue; + } + + var key = TagKey.create(Registries.STRUCTURE, location); + var set = registry.getTag(key).orElse(null); + if (set == null) { + Iris.error("Could not find structure tag: " + raw); + continue; + } + for (var holder : set) { + var resourceKey = holder.unwrapKey().orElse(null); + if (resourceKey == null) continue; + structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey()); + } + } catch (Throwable e) { + Iris.error("Failed to load structure: " + s.getLoadKey()); + e.printStackTrace(); + } + } + } + + private void addAll(KList placements, KSet structures) { + if (placements == null) return; + placements.stream() + .map(IrisJigsawStructurePlacement::getStructure) + .map(engine.getData().getJigsawStructureLoader()::load) + .filter(Objects::nonNull) + .forEach(structures::add); + } + + @Override + public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (engine.getDimension().isDisableExplorerMaps()) + return null; + + KMap> structures = new KMap<>(); + for (var holder : holders) { + if (holder == null) continue; + var key = holder.unwrapKey().orElse(null); + var set = this.structures.get(key); + if (set == null) continue; + for (var structure : set) { + structures.put(structure, holder); + } + } + if (structures.isEmpty()) + return null; + + var locator = ResultLocator.locateStructure(structures.keySet()) + .then((e, p , s) -> structures.get(s.getLoadKey())); + if (findUnexplored) + locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s); + + try { + var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get(); + if (result == null) return null; + var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ()); + return Pair.of(blockPos, result.obj()); + } catch (WrongEngineBroException | ExecutionException | InterruptedException e) { + return null; + } + } + + @Override + protected Codec codec() { + return Codec.unit(null); + } + + @Override + public ChunkGenerator getDelegate() { + if (delegate instanceof CustomChunkGenerator chunkGenerator) + return chunkGenerator.getDelegate(); + return delegate; + } + + @Override + public BiomeSource getBiomeSource() { + return biomeSource; + } + + @Override + public int getMinY() { + return delegate.getMinY(); + } + + @Override + public int getSeaLevel() { + return delegate.getSeaLevel(); + } + + @Override + public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) { + delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager); + } + + @Override + public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) { + delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess); + } + + @Override + public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess, GenerationStep.Carving worldgenstage_features) { + delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess, worldgenstage_features); + } + + @Override + public CompletableFuture fillFromNoise(Executor executor, Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) { + return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess); + } + + @Override + public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public WeightedRandomList getMobsAt(Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { + return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition); + } + + @Override + public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager); + } + + @Override + public void addDebugScreenInfo(List list, RandomState randomstate, BlockPos blockposition) { + delegate.addDebugScreenInfo(list, randomstate, blockposition); + } + + @Override + public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) { + delegate.spawnOriginalMobs(regionlimitedworldaccess); + } + + @Override + public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) { + return delegate.getSpawnHeight(levelheightaccessor); + } + + @Override + public int getGenDepth() { + return delegate.getGenDepth(); + } + + @Override + public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate); + } +} 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 cb8323009..97d1175d6 100644 --- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java @@ -1,6 +1,7 @@ package com.volmit.iris.core.nms.v1_20_R1; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.datafixers.util.Pair; import com.volmit.iris.Iris; import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.BiomeColor; @@ -27,6 +28,7 @@ import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.TagParser; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.TagKey; import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.biome.BiomeSource; @@ -478,12 +480,6 @@ public class NMSBinding implements INMSBinding { } } - public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) { - CraftDolphin cd = (CraftDolphin)dolphin; - cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ())); - cd.getHandle().setGotFish(true); - } - public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { Field[] fields = net.minecraft.world.entity.EntityType.class.getDeclaredFields(); for (Field field : fields) { @@ -512,17 +508,9 @@ public class NMSBinding implements INMSBinding { return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason); } - public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { - ServerLevel serverLevel = ((CraftWorld)world).getHandle(); - Class clazz = serverLevel.getChunkSource().chunkMap.generator.getClass(); - Field biomeSource = getField(clazz, BiomeSource.class); - biomeSource.setAccessible(true); - Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - unsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe)unsafeField.get(null); - CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world); - unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource); - biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); + public void inject(long seed, Engine engine, World world) { + var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap; + chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world); } @Override @@ -549,6 +537,23 @@ public class NMSBinding implements INMSBinding { return new Color(rgba, true); } + @Override + public KList getStructureKeys() { + KList keys = new KList<>(); + + var registry = registry().registry(Registries.STRUCTURE).orElse(null); + if (registry == null) return keys; + registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add); + registry.getTags() + .map(Pair::getFirst) + .map(TagKey::location) + .map(ResourceLocation::toString) + .map(s -> "#" + s) + .forEach(keys::add); + + return keys; + } + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { try { for (Field f : clazz.getDeclaredFields()) { diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/IrisChunkGenerator.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/IrisChunkGenerator.java new file mode 100644 index 000000000..27afcb0f7 --- /dev/null +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/IrisChunkGenerator.java @@ -0,0 +1,241 @@ +package com.volmit.iris.core.nms.v1_20_R2; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.ResultLocator; +import com.volmit.iris.engine.framework.WrongEngineBroException; +import com.volmit.iris.engine.object.IrisJigsawStructure; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.math.Position2; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.TagKey; +import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.NoiseColumn; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R2.generator.CustomChunkGenerator; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +public class IrisChunkGenerator extends CustomChunkGenerator { + private final ChunkGenerator delegate; + private final Engine engine; + private final BiomeSource biomeSource; + private final KMap, KSet> structures = new KMap<>(); + + public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { + super(((CraftWorld) world).getHandle(), delegate, null); + this.delegate = delegate; + this.engine = engine; + this.biomeSource = new CustomBiomeSource(seed, engine, world); + var dimension = engine.getDimension(); + + KSet placements = new KSet<>(); + addAll(dimension.getJigsawStructures(), placements); + for (var region : dimension.getAllRegions(engine)) { + addAll(region.getJigsawStructures(), placements); + for (var biome : region.getAllBiomes(engine)) + addAll(biome.getJigsawStructures(), placements); + } + var stronghold = dimension.getStronghold(); + if (stronghold != null) + placements.add(engine.getData().getJigsawStructureLoader().load(stronghold)); + placements.removeIf(Objects::isNull); + + var registry = ((CraftWorld) world).getHandle().registryAccess().registry(Registries.STRUCTURE).orElseThrow(); + for (var s : placements) { + try { + String raw = s.getStructureKey(); + if (raw == null) continue; + boolean tag = raw.startsWith("#"); + if (tag) raw = raw.substring(1); + + var location = new ResourceLocation(raw); + if (!tag) { + structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey()); + continue; + } + + var key = TagKey.create(Registries.STRUCTURE, location); + var set = registry.getTag(key).orElse(null); + if (set == null) { + Iris.error("Could not find structure tag: " + raw); + continue; + } + for (var holder : set) { + var resourceKey = holder.unwrapKey().orElse(null); + if (resourceKey == null) continue; + structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey()); + } + } catch (Throwable e) { + Iris.error("Failed to load structure: " + s.getLoadKey()); + e.printStackTrace(); + } + } + } + + private void addAll(KList placements, KSet structures) { + if (placements == null) return; + placements.stream() + .map(IrisJigsawStructurePlacement::getStructure) + .map(engine.getData().getJigsawStructureLoader()::load) + .filter(Objects::nonNull) + .forEach(structures::add); + } + + @Override + public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (engine.getDimension().isDisableExplorerMaps()) + return null; + + KMap> structures = new KMap<>(); + for (var holder : holders) { + if (holder == null) continue; + var key = holder.unwrapKey().orElse(null); + var set = this.structures.get(key); + if (set == null) continue; + for (var structure : set) { + structures.put(structure, holder); + } + } + if (structures.isEmpty()) + return null; + + var locator = ResultLocator.locateStructure(structures.keySet()) + .then((e, p , s) -> structures.get(s.getLoadKey())); + if (findUnexplored) + locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s); + + try { + var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get(); + if (result == null) return null; + var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ()); + return Pair.of(blockPos, result.obj()); + } catch (WrongEngineBroException | ExecutionException | InterruptedException e) { + return null; + } + } + + @Override + protected Codec codec() { + return Codec.unit(null); + } + + @Override + public ChunkGenerator getDelegate() { + if (delegate instanceof CustomChunkGenerator chunkGenerator) + return chunkGenerator.getDelegate(); + return delegate; + } + + @Override + public BiomeSource getBiomeSource() { + return biomeSource; + } + + @Override + public int getMinY() { + return delegate.getMinY(); + } + + @Override + public int getSeaLevel() { + return delegate.getSeaLevel(); + } + + @Override + public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) { + delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager); + } + + @Override + public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) { + delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess); + } + + @Override + public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess, GenerationStep.Carving worldgenstage_features) { + delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess, worldgenstage_features); + } + + @Override + public CompletableFuture fillFromNoise(Executor executor, Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) { + return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess); + } + + @Override + public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public WeightedRandomList getMobsAt(Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { + return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition); + } + + @Override + public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager); + } + + @Override + public void addDebugScreenInfo(List list, RandomState randomstate, BlockPos blockposition) { + delegate.addDebugScreenInfo(list, randomstate, blockposition); + } + + @Override + public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) { + delegate.spawnOriginalMobs(regionlimitedworldaccess); + } + + @Override + public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) { + return delegate.getSpawnHeight(levelheightaccessor); + } + + @Override + public int getGenDepth() { + return delegate.getGenDepth(); + } + + @Override + public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate); + } +} 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 9d073f4a7..187af289a 100644 --- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java @@ -13,7 +13,9 @@ import java.util.List; import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; +import com.mojang.datafixers.util.Pair; import com.volmit.iris.core.nms.container.BiomeColor; +import net.minecraft.tags.TagKey; import net.minecraft.world.level.LevelReader; import org.bukkit.*; import org.bukkit.block.Biome; @@ -476,23 +478,9 @@ public class NMSBinding implements INMSBinding { } } - public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) { - CraftDolphin cd = (CraftDolphin)dolphin; - cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ())); - cd.getHandle().setGotFish(true); - } - - public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { - ServerLevel serverLevel = ((CraftWorld)world).getHandle(); - Class clazz = serverLevel.getChunkSource().chunkMap.generator.getClass(); - Field biomeSource = getField(clazz, BiomeSource.class); - biomeSource.setAccessible(true); - Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - unsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe)unsafeField.get(null); - CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world); - unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource); - biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); + public void inject(long seed, Engine engine, World world) { + var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap; + chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world); } public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { @@ -547,6 +535,23 @@ public class NMSBinding implements INMSBinding { return new Color(rgba, true); } + @Override + public KList getStructureKeys() { + KList keys = new KList<>(); + + var registry = registry().registry(Registries.STRUCTURE).orElse(null); + if (registry == null) return keys; + registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add); + registry.getTags() + .map(Pair::getFirst) + .map(TagKey::location) + .map(ResourceLocation::toString) + .map(s -> "#" + s) + .forEach(keys::add); + + return keys; + } + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { try { for (Field f : clazz.getDeclaredFields()) { diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/IrisChunkGenerator.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/IrisChunkGenerator.java new file mode 100644 index 000000000..e61cbae71 --- /dev/null +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/IrisChunkGenerator.java @@ -0,0 +1,244 @@ +package com.volmit.iris.core.nms.v1_20_R3; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.ResultLocator; +import com.volmit.iris.engine.framework.WrongEngineBroException; +import com.volmit.iris.engine.object.IrisJigsawStructure; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.math.Position2; +import com.volmit.iris.util.scheduling.S; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.StructureTags; +import net.minecraft.tags.TagKey; +import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.item.EnderEyeItem; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.NoiseColumn; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R3.generator.CustomChunkGenerator; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +public class IrisChunkGenerator extends CustomChunkGenerator { + private final ChunkGenerator delegate; + private final Engine engine; + private final BiomeSource biomeSource; + private final KMap, KSet> structures = new KMap<>(); + + public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { + super(((CraftWorld) world).getHandle(), delegate, null); + this.delegate = delegate; + this.engine = engine; + this.biomeSource = new CustomBiomeSource(seed, engine, world); + var dimension = engine.getDimension(); + + KSet placements = new KSet<>(); + addAll(dimension.getJigsawStructures(), placements); + for (var region : dimension.getAllRegions(engine)) { + addAll(region.getJigsawStructures(), placements); + for (var biome : region.getAllBiomes(engine)) + addAll(biome.getJigsawStructures(), placements); + } + var stronghold = dimension.getStronghold(); + if (stronghold != null) + placements.add(engine.getData().getJigsawStructureLoader().load(stronghold)); + placements.removeIf(Objects::isNull); + + var registry = ((CraftWorld) world).getHandle().registryAccess().registry(Registries.STRUCTURE).orElseThrow(); + for (var s : placements) { + try { + String raw = s.getStructureKey(); + if (raw == null) continue; + boolean tag = raw.startsWith("#"); + if (tag) raw = raw.substring(1); + + var location = new ResourceLocation(raw); + if (!tag) { + structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey()); + continue; + } + + var key = TagKey.create(Registries.STRUCTURE, location); + var set = registry.getTag(key).orElse(null); + if (set == null) { + Iris.error("Could not find structure tag: " + raw); + continue; + } + for (var holder : set) { + var resourceKey = holder.unwrapKey().orElse(null); + if (resourceKey == null) continue; + structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey()); + } + } catch (Throwable e) { + Iris.error("Failed to load structure: " + s.getLoadKey()); + e.printStackTrace(); + } + } + } + + private void addAll(KList placements, KSet structures) { + if (placements == null) return; + placements.stream() + .map(IrisJigsawStructurePlacement::getStructure) + .map(engine.getData().getJigsawStructureLoader()::load) + .filter(Objects::nonNull) + .forEach(structures::add); + } + + @Override + public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (engine.getDimension().isDisableExplorerMaps()) + return null; + + KMap> structures = new KMap<>(); + for (var holder : holders) { + if (holder == null) continue; + var key = holder.unwrapKey().orElse(null); + var set = this.structures.get(key); + if (set == null) continue; + for (var structure : set) { + structures.put(structure, holder); + } + } + if (structures.isEmpty()) + return null; + + var locator = ResultLocator.locateStructure(structures.keySet()) + .then((e, p , s) -> structures.get(s.getLoadKey())); + if (findUnexplored) + locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s); + + try { + var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get(); + if (result == null) return null; + var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ()); + return Pair.of(blockPos, result.obj()); + } catch (WrongEngineBroException | ExecutionException | InterruptedException e) { + return null; + } + } + + @Override + protected Codec codec() { + return Codec.unit(null); + } + + @Override + public ChunkGenerator getDelegate() { + if (delegate instanceof CustomChunkGenerator chunkGenerator) + return chunkGenerator.getDelegate(); + return delegate; + } + + @Override + public BiomeSource getBiomeSource() { + return biomeSource; + } + + @Override + public int getMinY() { + return delegate.getMinY(); + } + + @Override + public int getSeaLevel() { + return delegate.getSeaLevel(); + } + + @Override + public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) { + delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager); + } + + @Override + public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) { + delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess); + } + + @Override + public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess, GenerationStep.Carving worldgenstage_features) { + delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess, worldgenstage_features); + } + + @Override + public CompletableFuture fillFromNoise(Executor executor, Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) { + return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess); + } + + @Override + public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public WeightedRandomList getMobsAt(Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { + return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition); + } + + @Override + public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager); + } + + @Override + public void addDebugScreenInfo(List list, RandomState randomstate, BlockPos blockposition) { + delegate.addDebugScreenInfo(list, randomstate, blockposition); + } + + @Override + public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) { + delegate.spawnOriginalMobs(regionlimitedworldaccess); + } + + @Override + public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) { + return delegate.getSpawnHeight(levelheightaccessor); + } + + @Override + public int getGenDepth() { + return delegate.getGenDepth(); + } + + @Override + public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate); + } +} 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 7334bb040..3b153a5dc 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 @@ -13,7 +13,9 @@ import java.util.List; import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; +import com.mojang.datafixers.util.Pair; import com.volmit.iris.core.nms.container.BiomeColor; +import net.minecraft.tags.TagKey; import net.minecraft.world.level.LevelReader; import org.bukkit.*; import org.bukkit.block.Biome; @@ -476,23 +478,9 @@ public class NMSBinding implements INMSBinding { } } - public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) { - CraftDolphin cd = (CraftDolphin)dolphin; - cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ())); - cd.getHandle().setGotFish(true); - } - - public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { - ServerLevel serverLevel = ((CraftWorld)world).getHandle(); - Class clazz = serverLevel.getChunkSource().chunkMap.generator.getClass(); - Field biomeSource = getField(clazz, BiomeSource.class); - biomeSource.setAccessible(true); - Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - unsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe)unsafeField.get(null); - CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world); - unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource); - biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); + public void inject(long seed, Engine engine, World world) { + var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap; + chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world); } public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { @@ -548,6 +536,23 @@ public class NMSBinding implements INMSBinding { return new Color(rgba, true); } + @Override + public KList getStructureKeys() { + KList keys = new KList<>(); + + var registry = registry().registry(Registries.STRUCTURE).orElse(null); + if (registry == null) return keys; + registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add); + registry.getTags() + .map(Pair::getFirst) + .map(TagKey::location) + .map(ResourceLocation::toString) + .map(s -> "#" + s) + .forEach(keys::add); + + return keys; + } + private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { try { for (Field f : clazz.getDeclaredFields()) { diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/IrisChunkGenerator.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/IrisChunkGenerator.java new file mode 100644 index 000000000..54382df77 --- /dev/null +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/IrisChunkGenerator.java @@ -0,0 +1,239 @@ +package com.volmit.iris.core.nms.v1_20_R4; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.MapCodec; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.ResultLocator; +import com.volmit.iris.engine.framework.WrongEngineBroException; +import com.volmit.iris.engine.object.IrisJigsawStructure; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.math.Position2; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.TagKey; +import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.NoiseColumn; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_20_R4.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R4.generator.CustomChunkGenerator; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.*; + +public class IrisChunkGenerator extends CustomChunkGenerator { + private final ChunkGenerator delegate; + private final Engine engine; + private final BiomeSource biomeSource; + private final KMap, KSet> structures = new KMap<>(); + + public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { + super(((CraftWorld) world).getHandle(), delegate, null); + this.delegate = delegate; + this.engine = engine; + this.biomeSource = new CustomBiomeSource(seed, engine, world); + var dimension = engine.getDimension(); + + KSet placements = new KSet<>(); + addAll(dimension.getJigsawStructures(), placements); + for (var region : dimension.getAllRegions(engine)) { + addAll(region.getJigsawStructures(), placements); + for (var biome : region.getAllBiomes(engine)) + addAll(biome.getJigsawStructures(), placements); + } + var stronghold = dimension.getStronghold(); + if (stronghold != null) + placements.add(engine.getData().getJigsawStructureLoader().load(stronghold)); + placements.removeIf(Objects::isNull); + + var registry = ((CraftWorld) world).getHandle().registryAccess().registry(Registries.STRUCTURE).orElseThrow(); + for (var s : placements) { + try { + String raw = s.getStructureKey(); + if (raw == null) continue; + boolean tag = raw.startsWith("#"); + if (tag) raw = raw.substring(1); + + var location = new ResourceLocation(raw); + if (!tag) { + structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey()); + continue; + } + + var key = TagKey.create(Registries.STRUCTURE, location); + var set = registry.getTag(key).orElse(null); + if (set == null) { + Iris.error("Could not find structure tag: " + raw); + continue; + } + for (var holder : set) { + var resourceKey = holder.unwrapKey().orElse(null); + if (resourceKey == null) continue; + structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey()); + } + } catch (Throwable e) { + Iris.error("Failed to load structure: " + s.getLoadKey()); + e.printStackTrace(); + } + } + } + + private void addAll(KList placements, KSet structures) { + if (placements == null) return; + placements.stream() + .map(IrisJigsawStructurePlacement::getStructure) + .map(engine.getData().getJigsawStructureLoader()::load) + .filter(Objects::nonNull) + .forEach(structures::add); + } + + @Override + public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (engine.getDimension().isDisableExplorerMaps()) + return null; + + KMap> structures = new KMap<>(); + for (var holder : holders) { + if (holder == null) continue; + var key = holder.unwrapKey().orElse(null); + var set = this.structures.get(key); + if (set == null) continue; + for (var structure : set) { + structures.put(structure, holder); + } + } + if (structures.isEmpty()) + return null; + + var locator = ResultLocator.locateStructure(structures.keySet()) + .then((e, p , s) -> structures.get(s.getLoadKey())); + if (findUnexplored) + locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s); + + try { + var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get(); + if (result == null) return null; + var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ()); + return Pair.of(blockPos, result.obj()); + } catch (WrongEngineBroException | ExecutionException | InterruptedException e) { + return null; + } + } + + @Override + protected MapCodec codec() { + return MapCodec.unit(null); + } + + @Override + public ChunkGenerator getDelegate() { + if (delegate instanceof CustomChunkGenerator chunkGenerator) + return chunkGenerator.getDelegate(); + return delegate; + } + + @Override + public BiomeSource getBiomeSource() { + return biomeSource; + } + + @Override + public int getMinY() { + return delegate.getMinY(); + } + + @Override + public int getSeaLevel() { + return delegate.getSeaLevel(); + } + + @Override + public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) { + delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager); + } + + @Override + public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) { + delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess); + } + + @Override + public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess, GenerationStep.Carving worldgenstage_features) { + delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess, worldgenstage_features); + } + + @Override + public CompletableFuture fillFromNoise(Executor executor, Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) { + return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess); + } + + @Override + public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public WeightedRandomList getMobsAt(Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { + return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition); + } + + @Override + public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager); + } + + @Override + public void addDebugScreenInfo(List list, RandomState randomstate, BlockPos blockposition) { + delegate.addDebugScreenInfo(list, randomstate, blockposition); + } + + @Override + public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) { + delegate.spawnOriginalMobs(regionlimitedworldaccess); + } + + @Override + public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) { + return delegate.getSpawnHeight(levelheightaccessor); + } + + @Override + public int getGenDepth() { + return delegate.getGenDepth(); + } + + @Override + public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate); + } +} 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 4b407ef18..2cf09de92 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 @@ -14,9 +14,12 @@ import java.util.Optional; import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; +import com.mojang.datafixers.util.Pair; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; import net.minecraft.core.component.DataComponents; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.TagKey; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.chunk.status.ChunkStatus; @@ -28,6 +31,7 @@ import org.bukkit.craftbukkit.v1_20_R4.CraftServer; import org.bukkit.craftbukkit.v1_20_R4.CraftWorld; import org.bukkit.craftbukkit.v1_20_R4.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R4.entity.CraftDolphin; +import org.bukkit.craftbukkit.v1_20_R4.generator.CustomChunkGenerator; import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_20_R4.util.CraftNamespacedKey; import org.bukkit.entity.Dolphin; @@ -64,15 +68,12 @@ import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.TagParser; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; -import sun.misc.Unsafe; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); @@ -480,23 +481,9 @@ public class NMSBinding implements INMSBinding { } } - public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) { - CraftDolphin cd = (CraftDolphin)dolphin; - cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ())); - cd.getHandle().setGotFish(true); - } - - public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { - ServerLevel serverLevel = ((CraftWorld)world).getHandle(); - Class clazz = serverLevel.getChunkSource().chunkMap.generator.getClass(); - Field biomeSource = getField(clazz, BiomeSource.class); - biomeSource.setAccessible(true); - Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - unsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe)unsafeField.get(null); - CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world); - unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource); - biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource); + public void inject(long seed, Engine engine, World world) { + var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap; + chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world); } public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { @@ -585,4 +572,21 @@ public class NMSBinding implements INMSBinding { if (radius == null) throw new IllegalStateException("GameRule.SPAWN_CHUNK_RADIUS is null!"); return (int) Math.pow(2 * radius + 1, 2); } + + @Override + public KList getStructureKeys() { + KList keys = new KList<>(); + + var registry = registry().registry(Registries.STRUCTURE).orElse(null); + if (registry == null) return keys; + registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add); + registry.getTags() + .map(Pair::getFirst) + .map(TagKey::location) + .map(ResourceLocation::toString) + .map(s -> "#" + s) + .forEach(keys::add); + + return keys; + } } diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/IrisChunkGenerator.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/IrisChunkGenerator.java new file mode 100644 index 000000000..872daac16 --- /dev/null +++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/IrisChunkGenerator.java @@ -0,0 +1,240 @@ +package com.volmit.iris.core.nms.v1_21_R1; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.MapCodec; +import com.volmit.iris.Iris; +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.framework.ResultLocator; +import com.volmit.iris.engine.framework.WrongEngineBroException; +import com.volmit.iris.engine.object.IrisJigsawStructure; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.collection.KSet; +import com.volmit.iris.util.mantle.MantleFlag; +import com.volmit.iris.util.math.Position2; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.tags.TagKey; +import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.NoiseColumn; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_21_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_21_R1.generator.CustomChunkGenerator; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class IrisChunkGenerator extends CustomChunkGenerator { + private final ChunkGenerator delegate; + private final Engine engine; + private final BiomeSource biomeSource; + private final KMap, KSet> structures = new KMap<>(); + + public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { + super(((CraftWorld) world).getHandle(), delegate, null); + this.delegate = delegate; + this.engine = engine; + this.biomeSource = new CustomBiomeSource(seed, engine, world); + var dimension = engine.getDimension(); + + KSet placements = new KSet<>(); + addAll(dimension.getJigsawStructures(), placements); + for (var region : dimension.getAllRegions(engine)) { + addAll(region.getJigsawStructures(), placements); + for (var biome : region.getAllBiomes(engine)) + addAll(biome.getJigsawStructures(), placements); + } + var stronghold = dimension.getStronghold(); + if (stronghold != null) + placements.add(engine.getData().getJigsawStructureLoader().load(stronghold)); + placements.removeIf(Objects::isNull); + + var registry = ((CraftWorld) world).getHandle().registryAccess().registry(Registries.STRUCTURE).orElseThrow(); + for (var s : placements) { + try { + String raw = s.getStructureKey(); + if (raw == null) continue; + boolean tag = raw.startsWith("#"); + if (tag) raw = raw.substring(1); + + var location = ResourceLocation.parse(raw); + if (!tag) { + structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey()); + continue; + } + + var key = TagKey.create(Registries.STRUCTURE, location); + var set = registry.getTag(key).orElse(null); + if (set == null) { + Iris.error("Could not find structure tag: " + raw); + continue; + } + for (var holder : set) { + var resourceKey = holder.unwrapKey().orElse(null); + if (resourceKey == null) continue; + structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey()); + } + } catch (Throwable e) { + Iris.error("Failed to load structure: " + s.getLoadKey()); + e.printStackTrace(); + } + } + } + + private void addAll(KList placements, KSet structures) { + if (placements == null) return; + placements.stream() + .map(IrisJigsawStructurePlacement::getStructure) + .map(engine.getData().getJigsawStructureLoader()::load) + .filter(Objects::nonNull) + .forEach(structures::add); + } + + @Override + public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { + if (engine.getDimension().isDisableExplorerMaps()) + return null; + + KMap> structures = new KMap<>(); + for (var holder : holders) { + if (holder == null) continue; + var key = holder.unwrapKey().orElse(null); + var set = this.structures.get(key); + if (set == null) continue; + for (var structure : set) { + structures.put(structure, holder); + } + } + if (structures.isEmpty()) + return null; + + var locator = ResultLocator.locateStructure(structures.keySet()) + .then((e, p , s) -> structures.get(s.getLoadKey())); + if (findUnexplored) + locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s); + + try { + var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get(); + if (result == null) return null; + var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ()); + return Pair.of(blockPos, result.obj()); + } catch (WrongEngineBroException | ExecutionException | InterruptedException e) { + return null; + } + } + + @Override + protected MapCodec codec() { + return MapCodec.unit(null); + } + + @Override + public ChunkGenerator getDelegate() { + if (delegate instanceof CustomChunkGenerator chunkGenerator) + return chunkGenerator.getDelegate(); + return delegate; + } + + @Override + public BiomeSource getBiomeSource() { + return biomeSource; + } + + @Override + public int getMinY() { + return delegate.getMinY(); + } + + @Override + public int getSeaLevel() { + return delegate.getSeaLevel(); + } + + @Override + public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) { + delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager); + } + + @Override + public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) { + delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess); + } + + @Override + public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess, GenerationStep.Carving worldgenstage_features) { + delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess, worldgenstage_features); + } + + @Override + public CompletableFuture fillFromNoise(Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) { + return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess); + } + + @Override + public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + } + + @Override + public WeightedRandomList getMobsAt(Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { + return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition); + } + + @Override + public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager); + } + + @Override + public void addDebugScreenInfo(List list, RandomState randomstate, BlockPos blockposition) { + delegate.addDebugScreenInfo(list, randomstate, blockposition); + } + + @Override + public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) { + delegate.spawnOriginalMobs(regionlimitedworldaccess); + } + + @Override + public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) { + return delegate.getSpawnHeight(levelheightaccessor); + } + + @Override + public int getGenDepth() { + return delegate.getGenDepth(); + } + + @Override + public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate); + } +} 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 1455b09e1..422131fbe 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 @@ -14,10 +14,12 @@ import java.util.Optional; import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; +import com.mojang.datafixers.util.Pair; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.datapack.DataVersion; import net.minecraft.core.component.DataComponents; import net.minecraft.server.level.ChunkMap; +import net.minecraft.tags.TagKey; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.chunk.status.ChunkStatus; @@ -482,26 +484,17 @@ public class NMSBinding implements INMSBinding { } } - public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) { - CraftDolphin cd = (CraftDolphin)dolphin; - cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ())); - cd.getHandle().setGotFish(true); - } - public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap; var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class); worldGenContextField.setAccessible(true); var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap); - Class clazz = worldGenContext.generator().getClass(); - Field biomeSource = getField(clazz, BiomeSource.class); - biomeSource.setAccessible(true); - Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - unsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe)unsafeField.get(null); - CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world); - unsafe.putObject(biomeSource.get(worldGenContext.generator()), unsafe.objectFieldOffset(biomeSource), customBiomeSource); - biomeSource.set(worldGenContext.generator(), customBiomeSource); + + var newContext = new WorldGenContext( + worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world), + worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadMailBox()); + + worldGenContextField.set(chunkMap, newContext); } public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { @@ -590,4 +583,21 @@ public class NMSBinding implements INMSBinding { if (radius == null) throw new IllegalStateException("GameRule.SPAWN_CHUNK_RADIUS is null!"); return (int) Math.pow(2 * radius + 1, 2); } + + @Override + public KList getStructureKeys() { + KList keys = new KList<>(); + + var registry = registry().registry(Registries.STRUCTURE).orElse(null); + if (registry == null) return keys; + registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add); + registry.getTags() + .map(Pair::getFirst) + .map(TagKey::location) + .map(ResourceLocation::toString) + .map(s -> "#" + s) + .forEach(keys::add); + + return keys; + } }