From 61dae5b223c76337a1e1bd3a8941631e886bd9b4 Mon Sep 17 00:00:00 2001 From: DerFrZocker Date: Fri, 1 Jul 2022 20:41:02 +1000 Subject: [PATCH] SPIGOT-7011, SPIGOT-7065: Overhaul of structures --- src/main/java/org/bukkit/Bukkit.java | 17 ++++ src/main/java/org/bukkit/Registry.java | 14 ++++ src/main/java/org/bukkit/Server.java | 15 ++++ src/main/java/org/bukkit/StructureType.java | 5 ++ src/main/java/org/bukkit/World.java | 78 ++++++++++++++++++- .../bukkit/generator/structure/Structure.java | 61 +++++++++++++++ .../generator/structure/StructureType.java | 37 +++++++++ .../generator/structure/package-info.java | 5 ++ .../bukkit/util/StructureSearchResult.java | 32 ++++++++ src/test/java/org/bukkit/TestServer.java | 24 ++++++ src/test/java/org/bukkit/TestWorld.java | 2 + 11 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/bukkit/generator/structure/Structure.java create mode 100644 src/main/java/org/bukkit/generator/structure/StructureType.java create mode 100644 src/main/java/org/bukkit/generator/structure/package-info.java create mode 100644 src/main/java/org/bukkit/util/StructureSearchResult.java diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java index edeaa5d5..7922d65d 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java @@ -1903,6 +1903,23 @@ public final class Bukkit { return server.getStructureManager(); } + /** + * Returns the registry for the given class. + *
+ * If no registry is present for the given class null will be returned. + *
+ * Depending on the implementation not every registry present in + * {@link Registry} will be returned by this method. + * + * @param tClass of the registry to get + * @param type of the registry + * @return the corresponding registry or null if not present + */ + @Nullable + public static Registry getRegistry(@NotNull Class tClass) { + return server.getRegistry(tClass); + } + /** * @return the unsafe values instance * @see UnsafeValues diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java index 2796c537..a7732025 100644 --- a/src/main/java/org/bukkit/Registry.java +++ b/src/main/java/org/bukkit/Registry.java @@ -15,6 +15,8 @@ import org.bukkit.entity.EntityType; import org.bukkit.entity.Frog; import org.bukkit.entity.Villager; import org.bukkit.entity.memory.MemoryKey; +import org.bukkit.generator.structure.Structure; +import org.bukkit.generator.structure.StructureType; import org.bukkit.loot.LootTables; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -128,6 +130,18 @@ public interface Registry extends Iterable { * @see Statistic */ Registry STATISTIC = new SimpleRegistry<>(Statistic.class); + /** + * Server structures. + * + * @see Structure + */ + Registry STRUCTURE = Bukkit.getRegistry(Structure.class); + /** + * Server structure types. + * + * @see StructureType + */ + Registry STRUCTURE_TYPE = Bukkit.getRegistry(StructureType.class); /** * Sound keys. * diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java index 804453f5..01c01876 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java @@ -1615,6 +1615,21 @@ public interface Server extends PluginMessageRecipient { @NotNull StructureManager getStructureManager(); + /** + * Returns the registry for the given class. + *
+ * If no registry is present for the given class null will be returned. + *
+ * Depending on the implementation not every registry present in + * {@link Registry} will be returned by this method. + * + * @param tClass of the registry to get + * @param type of the registry + * @return the corresponding registry or null if not present + */ + @Nullable + Registry getRegistry(@NotNull Class tClass); + /** * @return the unsafe values instance * @see UnsafeValues diff --git a/src/main/java/org/bukkit/StructureType.java b/src/main/java/org/bukkit/StructureType.java index ce4ffebc..7707def6 100644 --- a/src/main/java/org/bukkit/StructureType.java +++ b/src/main/java/org/bukkit/StructureType.java @@ -17,8 +17,13 @@ import org.jetbrains.annotations.Nullable; * Mansions, etc. *
* The registration of new {@link StructureType}s is case-sensitive. + * + * @deprecated This class does not represent the structures of a world well. Use + * {@link org.bukkit.generator.structure.Structure} or + * {@link org.bukkit.generator.structure.StructureType} instead. */ // Order is retrieved from WorldGenFactory +@Deprecated public final class StructureType implements Keyed { private static final Map structureTypeMap = new HashMap<>(); diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java index d1bb770e..a2774163 100644 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java @@ -23,6 +23,8 @@ import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.WorldInfo; +import org.bukkit.generator.structure.Structure; +import org.bukkit.generator.structure.StructureType; import org.bukkit.inventory.ItemStack; import org.bukkit.material.MaterialData; import org.bukkit.metadata.Metadatable; @@ -32,6 +34,7 @@ import org.bukkit.plugin.messaging.PluginMessageRecipient; import org.bukkit.util.BoundingBox; import org.bukkit.util.Consumer; import org.bukkit.util.RayTraceResult; +import org.bukkit.util.StructureSearchResult; import org.bukkit.util.Vector; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -2569,9 +2572,82 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * @param findUnexplored true to only find unexplored structures * @return the closest {@link Location}, or null if no structure of the * specified type exists. + * @see #locateNearestStructure(Location, Structure, int, boolean) + * @see #locateNearestStructure(Location, StructureType, int, boolean) + * @deprecated Use + * {@link #locateNearestStructure(Location, Structure, int, boolean)} or + * {@link #locateNearestStructure(Location, StructureType, int, boolean)} + * instead. */ @Nullable - public Location locateNearestStructure(@NotNull Location origin, @NotNull StructureType structureType, int radius, boolean findUnexplored); + @Deprecated + public Location locateNearestStructure(@NotNull Location origin, @NotNull org.bukkit.StructureType structureType, int radius, boolean findUnexplored); + + /** + * Find the closest nearby structure of a given {@link StructureType}. + * Finding unexplored structures can, and will, block if the world is + * looking in chunks that gave not generated yet. This can lead to the world + * temporarily freezing while locating an unexplored structure. + *

+ * The {@code radius} is not a rigid square radius. Each structure may alter + * how many chunks to check for each iteration. Do not assume that only a + * radius x radius chunk area will be checked. For example, + * {@link StructureType#WOODLAND_MANSION} can potentially check up to 20,000 + * blocks away (or more) regardless of the radius used. + *

+ * This will not load or generate chunks. This can also lead to + * instances where the server can hang if you are only looking for + * unexplored structures. This is because it will keep looking further and + * further out in order to find the structure. + *

+ * The difference between searching for a {@link StructureType} and a + * {@link Structure} is, that a {@link StructureType} can refer to multiple + * {@link Structure Structures} while searching for a {@link Structure} + * while only search for the given {@link Structure}. + * + * @param origin where to start looking for a structure + * @param structureType the type of structure to find + * @param radius the radius, in chunks, around which to search + * @param findUnexplored true to only find unexplored structures + * @return the closest {@link Location} and {@link Structure}, or null if no + * structure of the specified type exists. + * @see #locateNearestStructure(Location, Structure, int, boolean) + */ + @Nullable + StructureSearchResult locateNearestStructure(@NotNull Location origin, @NotNull StructureType structureType, int radius, boolean findUnexplored); + + /** + * Find the closest nearby structure of a given {@link Structure}. Finding + * unexplored structures can, and will, block if the world is looking in + * chunks that gave not generated yet. This can lead to the world + * temporarily freezing while locating an unexplored structure. + *

+ * The {@code radius} is not a rigid square radius. Each structure may alter + * how many chunks to check for each iteration. Do not assume that only a + * radius x radius chunk area will be checked. For example, + * {@link Structure#MANSION} can potentially check up to 20,000 blocks away + * (or more) regardless of the radius used. + *

+ * This will not load or generate chunks. This can also lead to + * instances where the server can hang if you are only looking for + * unexplored structures. This is because it will keep looking further and + * further out in order to find the structure. + *

+ * The difference between searching for a {@link StructureType} and a + * {@link Structure} is, that a {@link StructureType} can refer to multiple + * {@link Structure Structures} while searching for a {@link Structure} + * while only search for the given {@link Structure}. + * + * @param origin where to start looking for a structure + * @param structure the structure to find + * @param radius the radius, in chunks, around which to search + * @param findUnexplored true to only find unexplored structures + * @return the closest {@link Location} and {@link Structure}, or null if no + * structure was found. + * @see #locateNearestStructure(Location, StructureType, int, boolean) + */ + @Nullable + StructureSearchResult locateNearestStructure(@NotNull Location origin, @NotNull Structure structure, int radius, boolean findUnexplored); /** * Finds the nearest raid close to the given location. diff --git a/src/main/java/org/bukkit/generator/structure/Structure.java b/src/main/java/org/bukkit/generator/structure/Structure.java new file mode 100644 index 00000000..ff7708a3 --- /dev/null +++ b/src/main/java/org/bukkit/generator/structure/Structure.java @@ -0,0 +1,61 @@ +package org.bukkit.generator.structure; + +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.jetbrains.annotations.NotNull; + +/** + * Represent a Structure from the world. + * + * Listed structures are present in the default server. Depending on the server + * there might be additional structures present (for example structures added by + * data packs), which can be received via {@link Registry#STRUCTURE}. + */ +public abstract class Structure implements Keyed { + + public static final Structure PILLAGER_OUTPOST = getStructure("pillager_outpost"); + public static final Structure MINESHAFT = getStructure("mineshaft"); + public static final Structure MINESHAFT_MESA = getStructure("mineshaft_mesa"); + public static final Structure MANSION = getStructure("mansion"); + public static final Structure JUNGLE_PYRAMID = getStructure("jungle_pyramid"); + public static final Structure DESERT_PYRAMID = getStructure("desert_pyramid"); + public static final Structure IGLOO = getStructure("igloo"); + public static final Structure SHIPWRECK = getStructure("shipwreck"); + public static final Structure SHIPWRECK_BEACHED = getStructure("shipwreck_beached"); + public static final Structure SWAMP_HUT = getStructure("swamp_hut"); + public static final Structure STRONGHOLD = getStructure("stronghold"); + public static final Structure MONUMENT = getStructure("monument"); + public static final Structure OCEAN_RUIN_COLD = getStructure("ocean_ruin_cold"); + public static final Structure OCEAN_RUIN_WARM = getStructure("ocean_ruin_warm"); + public static final Structure FORTRESS = getStructure("fortress"); + public static final Structure NETHER_FOSSIL = getStructure("nether_fossil"); + public static final Structure END_CITY = getStructure("end_city"); + public static final Structure BURIED_TREASURE = getStructure("buried_treasure"); + public static final Structure BASTION_REMNANT = getStructure("bastion_remnant"); + public static final Structure VILLAGE_PLAINS = getStructure("village_plains"); + public static final Structure VILLAGE_DESERT = getStructure("village_desert"); + public static final Structure VILLAGE_SAVANNA = getStructure("village_savanna"); + public static final Structure VILLAGE_SNOWY = getStructure("village_snowy"); + public static final Structure VILLAGE_TAIGA = getStructure("village_taiga"); + public static final Structure RUINED_PORTAL = getStructure("ruined_portal"); + public static final Structure RUINED_PORTAL_DESERT = getStructure("ruined_portal_desert"); + public static final Structure RUINED_PORTAL_JUNGLE = getStructure("ruined_portal_jungle"); + public static final Structure RUINED_PORTAL_SWAMP = getStructure("ruined_portal_swamp"); + public static final Structure RUINED_PORTAL_MOUNTAIN = getStructure("ruined_portal_mountain"); + public static final Structure RUINED_PORTAL_OCEAN = getStructure("ruined_portal_ocean"); + public static final Structure RUINED_PORTAL_NETHER = getStructure("ruined_portal_nether"); + public static final Structure ANCIENT_CITY = getStructure("ancient_city"); + + private static Structure getStructure(String name) { + return Registry.STRUCTURE.get(NamespacedKey.minecraft(name)); + } + + /** + * Returns the type of the structure. + * + * @return the type of structure + */ + @NotNull + public abstract StructureType getStructureType(); +} diff --git a/src/main/java/org/bukkit/generator/structure/StructureType.java b/src/main/java/org/bukkit/generator/structure/StructureType.java new file mode 100644 index 00000000..2f908d55 --- /dev/null +++ b/src/main/java/org/bukkit/generator/structure/StructureType.java @@ -0,0 +1,37 @@ +package org.bukkit.generator.structure; + +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; + +/** + * Represent a StructureType of a {@link Structure}. + * + * Listed structure types are present in the default server. Depending on the + * server there might be additional structure types present (for example + * structure types added by data packs), which can be received via + * {@link Registry#STRUCTURE_TYPE}. + */ +public abstract class StructureType implements Keyed { + + public static final StructureType BURIED_TREASURE = getStructureType("buried_treasure"); + public static final StructureType DESERT_PYRAMID = getStructureType("desert_pyramid"); + public static final StructureType END_CITY = getStructureType("end_city"); + public static final StructureType FORTRESS = getStructureType("fortress"); + public static final StructureType IGLOO = getStructureType("igloo"); + public static final StructureType JIGSAW = getStructureType("jigsaw"); + public static final StructureType JUNGLE_TEMPLE = getStructureType("jungle_temple"); + public static final StructureType MINESHAFT = getStructureType("mineshaft"); + public static final StructureType NETHER_FOSSIL = getStructureType("nether_fossil"); + public static final StructureType OCEAN_MONUMENT = getStructureType("ocean_monument"); + public static final StructureType OCEAN_RUIN = getStructureType("ocean_ruin"); + public static final StructureType RUINED_PORTAL = getStructureType("ruined_portal"); + public static final StructureType SHIPWRECK = getStructureType("shipwreck"); + public static final StructureType STRONGHOLD = getStructureType("stronghold"); + public static final StructureType SWAMP_HUT = getStructureType("swamp_hut"); + public static final StructureType WOODLAND_MANSION = getStructureType("woodland_mansion"); + + private static StructureType getStructureType(String name) { + return Registry.STRUCTURE_TYPE.get(NamespacedKey.minecraft(name)); + } +} diff --git a/src/main/java/org/bukkit/generator/structure/package-info.java b/src/main/java/org/bukkit/generator/structure/package-info.java new file mode 100644 index 00000000..d0159889 --- /dev/null +++ b/src/main/java/org/bukkit/generator/structure/package-info.java @@ -0,0 +1,5 @@ +/** + * Classes to facilitate world {@link org.bukkit.generator.structure.Structure} + * generation. + */ +package org.bukkit.generator.structure; diff --git a/src/main/java/org/bukkit/util/StructureSearchResult.java b/src/main/java/org/bukkit/util/StructureSearchResult.java new file mode 100644 index 00000000..81833d3b --- /dev/null +++ b/src/main/java/org/bukkit/util/StructureSearchResult.java @@ -0,0 +1,32 @@ +package org.bukkit.util; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.generator.structure.Structure; +import org.bukkit.generator.structure.StructureType; +import org.jetbrains.annotations.NotNull; + +/** + * Holds the result of searching for a structure. + * + * @see World#locateNearestStructure(Location, Structure, int, boolean) + * @see World#locateNearestStructure(Location, StructureType, int, boolean) + */ +public interface StructureSearchResult { + + /** + * Return the structure which was found. + * + * @return the found structure. + */ + @NotNull + Structure getStructure(); + + /** + * Return the location of the structure. + * + * @return the location the structure was found. + */ + @NotNull + Location getLocation(); +} diff --git a/src/test/java/org/bukkit/TestServer.java b/src/test/java/org/bukkit/TestServer.java index 61993528..622cf46a 100644 --- a/src/test/java/org/bukkit/TestServer.java +++ b/src/test/java/org/bukkit/TestServer.java @@ -4,11 +4,14 @@ import com.google.common.collect.ImmutableMap; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.Iterator; import java.util.Map; import java.util.logging.Logger; import org.bukkit.command.SimpleCommandMap; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.SimplePluginManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public final class TestServer implements InvocationHandler { private static interface MethodHandler { @@ -75,6 +78,27 @@ public final class TestServer implements InvocationHandler { } } ); + methodMap.put( + Server.class.getMethod("getRegistry", Class.class), + new MethodHandler() { + @Override + public Object handle(TestServer server, Object[] args) { + return new Registry() { + @NotNull + @Override + public Iterator iterator() { + return null; + } + + @Nullable + @Override + public Keyed get(@NotNull NamespacedKey key) { + return null; + } + }; + } + } + ); methods = methodMap.build(); TestServer server = new TestServer(); diff --git a/src/test/java/org/bukkit/TestWorld.java b/src/test/java/org/bukkit/TestWorld.java index beb15c7c..ab34f119 100644 --- a/src/test/java/org/bukkit/TestWorld.java +++ b/src/test/java/org/bukkit/TestWorld.java @@ -18,6 +18,8 @@ public final class TestWorld implements InvocationHandler { static { try { + TestServer.getInstance(); + ImmutableMap.Builder methodMap = ImmutableMap.builder(); methodMap.put( Object.class.getMethod("equals", Object.class),