+ * 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