From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: dfsek Date: Tue, 15 Sep 2020 21:59:16 -0700 Subject: [PATCH] Add StructuresLocateEvent Co-authored-by: Jake Potrebic diff --git a/src/main/java/io/papermc/paper/event/world/StructureLocateEvent.java b/src/main/java/io/papermc/paper/event/world/StructureLocateEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..1ac3369455972aeb1ade5dc023d1f818cd3535fa --- /dev/null +++ b/src/main/java/io/papermc/paper/event/world/StructureLocateEvent.java @@ -0,0 +1,163 @@ +package io.papermc.paper.event.world; + +import org.bukkit.Location; +import org.bukkit.StructureType; +import org.bukkit.World; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.world.WorldEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Called before a structure/feature is located. + * This happens when: + * + * + * @deprecated no longer used, see {@link StructuresLocateEvent} + */ +@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") +public class StructureLocateEvent extends WorldEvent implements Cancellable { + + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final Location origin; + private Location result = null; + private StructureType type; + private int radius; + private boolean findUnexplored; + + private boolean cancelled; + + @ApiStatus.Internal + public StructureLocateEvent(@NotNull World world, @NotNull Location origin, @NotNull StructureType structureType, int radius, boolean findUnexplored) { + super(world); + this.origin = origin; + this.type = structureType; + this.radius = radius; + this.findUnexplored = findUnexplored; + } + + /** + * Gets the location set as the structure location, if it was defined. + *

+ * Returns {@code null} if it has not been set by {@link StructureLocateEvent#setResult(Location)}. + * Since this event fires before the search is done, the actual location is unknown at this point. + * + * @return The result location, if it has been set. {@code null} if it has not. + * @see World#locateNearestStructure(Location, StructureType, int, boolean) + */ + @Nullable + public Location getResult() { + return this.result; + } + + /** + * Sets the result {@link Location}. This causes the search to be skipped, and the location passed here to be used as the result. + * + * @param result the {@link Location} of the structure. + */ + public void setResult(@Nullable Location result) { + this.result = result; + } + + /** + * Gets the {@link StructureType} that is to be located. + * + * @return the structure type. + */ + @NotNull + public StructureType getType() { + return this.type; + } + + /** + * Sets the {@link StructureType} that is to be located. + * + * @param type the structure type. + */ + public void setType(@NotNull StructureType type) { + this.type = type; + } + + /** + * Gets the {@link Location} from which the search is to be conducted. + * + * @return {@link Location} where search begins + */ + @NotNull + public Location getOrigin() { + return this.origin; + } + + /** + * Gets the search radius in which to attempt locating the structure. + *

+ * This radius may not always be obeyed during the structure search! + * + * @return the search radius. + */ + public int getRadius() { + return this.radius; + } + + /** + * Sets the search radius in which to attempt locating the structure. + *

+ * This radius may not always be obeyed during the structure search! + * + * @param radius the search radius. + */ + public void setRadius(int radius) { + this.radius = radius; + } + + /** + * Gets whether to search exclusively for unexplored structures. + *

+ * As with the search radius, this value is not always obeyed. + * + * @return Whether to search for only unexplored structures. + */ + public boolean shouldFindUnexplored() { + return this.findUnexplored; + } + + /** + * Sets whether to search exclusively for unexplored structures. + *

+ * As with the search radius, this value is not always obeyed. + * + * @param findUnexplored Whether to search for only unexplored structures. + */ + public void setFindUnexplored(boolean findUnexplored) { + this.findUnexplored = findUnexplored; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @NotNull + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } +} diff --git a/src/main/java/io/papermc/paper/event/world/StructuresLocateEvent.java b/src/main/java/io/papermc/paper/event/world/StructuresLocateEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..582af444b058708638683e7d6f9b79685c04c061 --- /dev/null +++ b/src/main/java/io/papermc/paper/event/world/StructuresLocateEvent.java @@ -0,0 +1,213 @@ +package io.papermc.paper.event.world; + +import io.papermc.paper.math.Position; +import io.papermc.paper.util.TransformingRandomAccessList; +import io.papermc.paper.world.structure.ConfiguredStructure; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.world.WorldEvent; +import org.bukkit.generator.structure.Structure; +import org.bukkit.generator.structure.StructureType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +/** + * Called before a set of configured structures is located. + * This happens when: + *

+ */ +public class StructuresLocateEvent extends WorldEvent implements Cancellable { + + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final Location origin; + private Result result; + private List structures; + private List legacy$structures; + private int radius; + private boolean findUnexplored; + + private boolean cancelled; + + @ApiStatus.Internal + public StructuresLocateEvent(@NotNull World world, @NotNull Location origin, @NotNull List structures, int radius, boolean findUnexplored) { + super(world); + this.origin = origin; + this.setStructures(structures); + this.radius = radius; + this.findUnexplored = findUnexplored; + } + + /** + * Gets the {@link Location} from which the search is to be conducted. + * + * @return {@link Location} where search begins + */ + public @NotNull Location getOrigin() { + return this.origin.clone(); + } + + /** + * Gets the {@link Location} and {@link Structure} set as the result, if it was defined. + *

+ * Returns {@code null} if it has not been set by {@link StructuresLocateEvent#setResult(Result)}. + * Since this event fires before the search is done, the actual result is unknown at this point. + * + * @return The result location and structure, if it has been set. {@code null} if it has not. + * @see World#locateNearestStructure(Location, StructureType, int, boolean) + */ + public @Nullable Result getResult() { + return this.result; + } + + /** + * Sets the result {@link Location} and {@link Structure}. This causes the search to be + * skipped, and the result object passed here to be used as the result. + * + * @param result the {@link Location} and {@link Structure} of the search. + */ + public void setResult(@Nullable Result result) { + this.result = result; + } + + /** + * Gets a mutable list of ConfiguredStructures that are valid targets for the search. + * + * @return a mutable list of ConfiguredStructures + * @deprecated use {@link #getStructures()} + */ + @Deprecated(forRemoval = true) + public @NotNull List getConfiguredStructures() { + return this.legacy$structures; + } + + /** + * Sets the list of ConfiguredStructures that are valid targets for the search. + * + * @param configuredStructures a list of ConfiguredStructure targets + * @deprecated use {@link #setStructures(List)} + */ + @Deprecated(forRemoval = true) + public void setConfiguredStructures(@NotNull List configuredStructures) { + this.setStructures(configuredStructures.stream().map(ConfiguredStructure::toModern).toList()); + } + + /** + * Gets an unmodifiable list of Structures that are valid targets for the search. + * + * @return an unmodifiable list of Structures + */ + public @NotNull @UnmodifiableView List getStructures() { + return Collections.unmodifiableList(this.structures); + } + + /** + * Sets the list of Structures that are valid targets for the search. + * + * @param structures a list of Structures targets + */ + public void setStructures(final @NotNull List structures) { + this.structures = structures; + this.legacy$structures = new TransformingRandomAccessList<>(this.structures, ConfiguredStructure::fromModern, ConfiguredStructure::toModern); + } + + /** + * Gets the search radius in which to attempt locating the structure. + *

+ * This radius may not always be obeyed during the structure search! + * + * @return the search radius (in chunks) + */ + public int getRadius() { + return this.radius; + } + + /** + * Sets the search radius in which to attempt locating the structure. + *

+ * This radius may not always be obeyed during the structure search! + * + * @param radius the search radius (in chunks) + */ + public void setRadius(int radius) { + this.radius = radius; + } + + /** + * Gets whether to search exclusively for unexplored structures. + *

+ * As with the search radius, this value is not always obeyed. + * + * @return Whether to search for only unexplored structures. + */ + public boolean shouldFindUnexplored() { + return this.findUnexplored; + } + + /** + * Sets whether to search exclusively for unexplored structures. + *

+ * As with the search radius, this value is not always obeyed. + * + * @param findUnexplored Whether to search for only unexplored structures. + */ + public void setFindUnexplored(boolean findUnexplored) { + this.findUnexplored = findUnexplored; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public @NotNull HandlerList getHandlers() { + return HANDLER_LIST; + } + + public static @NotNull HandlerList getHandlerList() { + return HANDLER_LIST; + } + + /** + * Result for {@link StructuresLocateEvent}. + */ + public record Result(@NotNull Position pos, @NotNull Structure structure) { + + @Deprecated(forRemoval = true) + public Result(final @NotNull Location position, @NotNull ConfiguredStructure configuredStructure) { + this(position, configuredStructure.toModern()); + } + + @Deprecated(forRemoval = true) + public @NotNull ConfiguredStructure configuredStructure() { + return Objects.requireNonNull(ConfiguredStructure.fromModern(this.structure), "Please use the newer Structure API"); + } + + @Deprecated(forRemoval = true) + public @NotNull Location position() { + //noinspection DataFlowIssue + return this.pos.toLocation(null); + } + } +} diff --git a/src/main/java/io/papermc/paper/world/structure/ConfiguredStructure.java b/src/main/java/io/papermc/paper/world/structure/ConfiguredStructure.java new file mode 100644 index 0000000000000000000000000000000000000000..1e7b53f9bc13dcd5a0a4a40004591e4f850496a0 --- /dev/null +++ b/src/main/java/io/papermc/paper/world/structure/ConfiguredStructure.java @@ -0,0 +1,113 @@ +package io.papermc.paper.world.structure; + +import io.papermc.paper.registry.Reference; +import java.util.Objects; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.StructureType; +import org.bukkit.generator.structure.Structure; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a configured structure each with a + * {@link StructureType}. Multiple ConfiguredStructures can have + * the same {@link StructureType}. + * @deprecated use {@link Structure} + */ +@Deprecated(forRemoval = true) +@ApiStatus.ScheduledForRemoval(inVersion = "1.21") +public final class ConfiguredStructure implements Keyed { + + public static final Reference PILLAGER_OUTPOST = create("pillager_outpost"); + public static final Reference MINESHAFT = create("mineshaft"); + public static final Reference MINESHAFT_MESA = create("mineshaft_mesa"); + public static final Reference WOODLAND_MANSION = create("mansion"); + public static final Reference JUNGLE_TEMPLE = create("jungle_pyramid"); + public static final Reference DESERT_PYRAMID = create("desert_pyramid"); + public static final Reference IGLOO = create("igloo"); + public static final Reference SHIPWRECK = create("shipwreck"); + public static final Reference SHIPWRECK_BEACHED = create("shipwreck_beached"); + public static final Reference SWAMP_HUT = create("swamp_hut"); + public static final Reference STRONGHOLD = create("stronghold"); + public static final Reference OCEAN_MONUMENT = create("monument"); + public static final Reference OCEAN_RUIN_COLD = create("ocean_ruin_cold"); + public static final Reference OCEAN_RUIN_WARM = create("ocean_ruin_warm"); + public static final Reference FORTRESS = create("fortress"); + public static final Reference NETHER_FOSSIL = create("nether_fossil"); + public static final Reference END_CITY = create("end_city"); + public static final Reference BURIED_TREASURE = create("buried_treasure"); + public static final Reference BASTION_REMNANT = create("bastion_remnant"); + public static final Reference VILLAGE_PLAINS = create("village_plains"); + public static final Reference VILLAGE_DESERT = create("village_desert"); + public static final Reference VILLAGE_SAVANNA = create("village_savanna"); + public static final Reference VILLAGE_SNOWY = create("village_snowy"); + public static final Reference VILLAGE_TAIGA = create("village_taiga"); + public static final Reference RUINED_PORTAL_STANDARD = create("ruined_portal"); + public static final Reference RUINED_PORTAL_DESERT = create("ruined_portal_desert"); + public static final Reference RUINED_PORTAL_JUNGLE = create("ruined_portal_jungle"); + public static final Reference RUINED_PORTAL_SWAMP = create("ruined_portal_swamp"); + public static final Reference RUINED_PORTAL_MOUNTAIN = create("ruined_portal_mountain"); + public static final Reference RUINED_PORTAL_OCEAN = create("ruined_portal_ocean"); + public static final Reference RUINED_PORTAL_NETHER = create("ruined_portal_nether"); + // public static final Reference ANCIENT_CITY = create("ancient_city"); // TODO remove when upstream adds "jigsaw" StructureType + + private final NamespacedKey key; + private final StructureType structureType; + + ConfiguredStructure(@NotNull NamespacedKey key, @NotNull StructureType structureType) { + this.key = key; + this.structureType = structureType; + } + + @Override + public @NotNull NamespacedKey getKey() { + return this.key; + } + + /** + * Gets the structure type for this configure structure. + * + * @return the structure type + */ + public @NotNull StructureType getStructureType() { + return this.structureType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ConfiguredStructure structure = (ConfiguredStructure) o; + return this.key.equals(structure.key) && this.structureType.equals(structure.structureType); + } + + @Override + public int hashCode() { + return Objects.hash(this.key, this.structureType); + } + + @Override + public String toString() { + return "ConfiguredStructure{" + + "key=" + this.key + + ", structureType=" + this.structureType + + '}'; + } + + private static @NotNull Reference create(@NotNull String name) { + return Reference.create(Registry.CONFIGURED_STRUCTURE, NamespacedKey.minecraft(name)); + } + + @ApiStatus.Internal + public @NotNull Structure toModern() { + return Objects.requireNonNull(Registry.STRUCTURE.get(this.key)); + } + + @ApiStatus.Internal + public static @Nullable ConfiguredStructure fromModern(@NotNull Structure structure) { + return Registry.CONFIGURED_STRUCTURE.get(structure.getKey()); + } +} diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java index 91117cad12eee0bdaac8e0398a0f7f288ba27a40..43f410326d6d68242113e2fefe31af256889ed9c 100644 --- a/src/main/java/org/bukkit/Registry.java +++ b/src/main/java/org/bukkit/Registry.java @@ -292,6 +292,15 @@ public interface Registry extends Iterable { * @see GameEvent */ Registry GAME_EVENT = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.GAME_EVENT); // Paper + // Paper start + /** + * Configured structures. + * @see io.papermc.paper.world.structure.ConfiguredStructure + * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#STRUCTURE} + */ + @Deprecated(forRemoval = true) + Registry CONFIGURED_STRUCTURE = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.world.structure.ConfiguredStructure.class), "No registry present for ConfiguredStructure. This is a bug."); + // Paper end /** * Get the object by its key. *