mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-27 01:29:32 +01:00
522 lines
20 KiB
Diff
522 lines
20 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: dfsek <dfsek@protonmail.com>
|
|
Date: Tue, 15 Sep 2020 21:59:16 -0700
|
|
Subject: [PATCH] Add StructuresLocateEvent
|
|
|
|
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
|
|
|
|
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..0c83a02059d65672ff191c42932d850950e9ea00
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/event/world/StructureLocateEvent.java
|
|
@@ -0,0 +1,157 @@
|
|
+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.NotNull;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+
|
|
+/**
|
|
+ * Called <b>before</b> a structure/feature is located.
|
|
+ * This happens when:
|
|
+ * <ul>
|
|
+ * <li>The /locate command is used.<br></li>
|
|
+ * <li>An Eye of Ender is used.</li>
|
|
+ * <li>An Explorer/Treasure Map is activated.</li>
|
|
+ * <li>{@link World#locateNearestStructure(Location, StructureType, int, boolean)} is invoked.</li>
|
|
+ * </ul>
|
|
+ * @deprecated no longer used, see {@link StructuresLocateEvent}
|
|
+ */
|
|
+@Deprecated(forRemoval = true)
|
|
+public class StructureLocateEvent extends WorldEvent implements Cancellable {
|
|
+ private static final HandlerList handlers = new HandlerList();
|
|
+ private final Location origin;
|
|
+ private Location result = null;
|
|
+ private StructureType type;
|
|
+ private int radius;
|
|
+ private boolean findUnexplored;
|
|
+ private boolean cancelled = false;
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ public static HandlerList getHandlerList() {
|
|
+ return handlers;
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ @Override
|
|
+ public HandlerList getHandlers() {
|
|
+ return handlers;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Gets the location set as the structure location, if it was defined.
|
|
+ * <p>
|
|
+ * Returns {@code null} if it has not been set by {@link StructureLocateEvent#setResult(Location)}.
|
|
+ * Since this event fires <i>before</i> the search is done, the actual location is unknown at this point.
|
|
+ *
|
|
+ * @return The result location, if it has been set. null if it has not.
|
|
+ * @see World#locateNearestStructure(Location, StructureType, int, boolean)
|
|
+ */
|
|
+ @Nullable
|
|
+ public Location getResult() {
|
|
+ return 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 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 origin;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Gets the search radius in which to attempt locating the structure.
|
|
+ * <p>
|
|
+ * This radius may not always be obeyed during the structure search!
|
|
+ *
|
|
+ * @return the search radius.
|
|
+ */
|
|
+ public int getRadius() {
|
|
+ return radius;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Sets the search radius in which to attempt locating the structure.
|
|
+ * <p>
|
|
+ * 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.
|
|
+ * <p>
|
|
+ * As with the search radius, this value is not always obeyed.
|
|
+ *
|
|
+ * @return Whether to search for only unexplored structures.
|
|
+ */
|
|
+ public boolean shouldFindUnexplored() {
|
|
+ return findUnexplored;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Sets whether to search exclusively for unexplored structures.
|
|
+ * <p>
|
|
+ * 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 cancelled;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setCancelled(boolean cancel) {
|
|
+ this.cancelled = cancel;
|
|
+ }
|
|
+}
|
|
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..1648ed317145eb73c0cca431823449b2adc43e04
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/event/world/StructuresLocateEvent.java
|
|
@@ -0,0 +1,208 @@
|
|
+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.jetbrains.annotations.NotNull;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+import org.jetbrains.annotations.UnmodifiableView;
|
|
+
|
|
+/**
|
|
+ * Called <b>before</b> a set of configured structures is located.
|
|
+ * This happens when:
|
|
+ * <ul>
|
|
+ * <li>The /locate command is used.<br></li>
|
|
+ * <li>An Eye of Ender is used.</li>
|
|
+ * <li>An Explorer/Treasure Map is activated.</li>
|
|
+ * <li>A dolphin swims to a treasure location.</li>
|
|
+ * <li>A trade is done with a villager for a map.</li>
|
|
+ * <li>{@link World#locateNearestStructure(Location, org.bukkit.StructureType, int, boolean)} is invoked.</li>
|
|
+ * </ul>
|
|
+ */
|
|
+public class StructuresLocateEvent extends WorldEvent implements Cancellable {
|
|
+
|
|
+ private static final HandlerList HANDLER_LIST = new HandlerList();
|
|
+
|
|
+ private final Location origin;
|
|
+ private Result result;
|
|
+ private List<Structure> structures;
|
|
+ private List<ConfiguredStructure> legacy$structures;
|
|
+ private int radius;
|
|
+ private boolean findUnexplored;
|
|
+ private boolean cancelled;
|
|
+
|
|
+ public StructuresLocateEvent(@NotNull World world, @NotNull Location origin, @NotNull List<Structure> 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;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Gets the {@link Location} and {@link Structure} set as the result, if it was defined.
|
|
+ * <p>
|
|
+ * Returns {@code null} if it has not been set by {@link StructuresLocateEvent#setResult(Result)}.
|
|
+ * Since this event fires <i>before</i> the search is done, the actual result is unknown at this point.
|
|
+ *
|
|
+ * @return The result location and structure, if it has been set. null if it has not.
|
|
+ * @see World#locateNearestStructure(Location, org.bukkit.generator.structure.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<ConfiguredStructure> 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<ConfiguredStructure> 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<Structure> 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<Structure> 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.
|
|
+ * <p>
|
|
+ * 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.
|
|
+ * <p>
|
|
+ * 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.
|
|
+ * <p>
|
|
+ * 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.
|
|
+ * <p>
|
|
+ * 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..b9a98f3c89cf847afd510bd1588d3a6d4b7eac0e
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/world/structure/ConfiguredStructure.java
|
|
@@ -0,0 +1,112 @@
|
|
+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)
|
|
+public final class ConfiguredStructure implements Keyed {
|
|
+
|
|
+ public static final Reference<ConfiguredStructure> PILLAGER_OUTPOST = create("pillager_outpost");
|
|
+ public static final Reference<ConfiguredStructure> MINESHAFT = create("mineshaft");
|
|
+ public static final Reference<ConfiguredStructure> MINESHAFT_MESA = create("mineshaft_mesa");
|
|
+ public static final Reference<ConfiguredStructure> WOODLAND_MANSION = create("mansion");
|
|
+ public static final Reference<ConfiguredStructure> JUNGLE_TEMPLE = create("jungle_pyramid");
|
|
+ public static final Reference<ConfiguredStructure> DESERT_PYRAMID = create("desert_pyramid");
|
|
+ public static final Reference<ConfiguredStructure> IGLOO = create("igloo");
|
|
+ public static final Reference<ConfiguredStructure> SHIPWRECK = create("shipwreck");
|
|
+ public static final Reference<ConfiguredStructure> SHIPWRECK_BEACHED = create("shipwreck_beached");
|
|
+ public static final Reference<ConfiguredStructure> SWAMP_HUT = create("swamp_hut");
|
|
+ public static final Reference<ConfiguredStructure> STRONGHOLD = create("stronghold");
|
|
+ public static final Reference<ConfiguredStructure> OCEAN_MONUMENT = create("monument");
|
|
+ public static final Reference<ConfiguredStructure> OCEAN_RUIN_COLD = create("ocean_ruin_cold");
|
|
+ public static final Reference<ConfiguredStructure> OCEAN_RUIN_WARM = create("ocean_ruin_warm");
|
|
+ public static final Reference<ConfiguredStructure> FORTRESS = create("fortress");
|
|
+ public static final Reference<ConfiguredStructure> NETHER_FOSSIL = create("nether_fossil");
|
|
+ public static final Reference<ConfiguredStructure> END_CITY = create("end_city");
|
|
+ public static final Reference<ConfiguredStructure> BURIED_TREASURE = create("buried_treasure");
|
|
+ public static final Reference<ConfiguredStructure> BASTION_REMNANT = create("bastion_remnant");
|
|
+ public static final Reference<ConfiguredStructure> VILLAGE_PLAINS = create("village_plains");
|
|
+ public static final Reference<ConfiguredStructure> VILLAGE_DESERT = create("village_desert");
|
|
+ public static final Reference<ConfiguredStructure> VILLAGE_SAVANNA = create("village_savanna");
|
|
+ public static final Reference<ConfiguredStructure> VILLAGE_SNOWY = create("village_snowy");
|
|
+ public static final Reference<ConfiguredStructure> VILLAGE_TAIGA = create("village_taiga");
|
|
+ public static final Reference<ConfiguredStructure> RUINED_PORTAL_STANDARD = create("ruined_portal");
|
|
+ public static final Reference<ConfiguredStructure> RUINED_PORTAL_DESERT = create("ruined_portal_desert");
|
|
+ public static final Reference<ConfiguredStructure> RUINED_PORTAL_JUNGLE = create("ruined_portal_jungle");
|
|
+ public static final Reference<ConfiguredStructure> RUINED_PORTAL_SWAMP = create("ruined_portal_swamp");
|
|
+ public static final Reference<ConfiguredStructure> RUINED_PORTAL_MOUNTAIN = create("ruined_portal_mountain");
|
|
+ public static final Reference<ConfiguredStructure> RUINED_PORTAL_OCEAN = create("ruined_portal_ocean");
|
|
+ public static final Reference<ConfiguredStructure> RUINED_PORTAL_NETHER = create("ruined_portal_nether");
|
|
+ // public static final Reference<ConfiguredStructure> 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<ConfiguredStructure> 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 ff1fcdaccbca81602278a0b52670f7b895ba22b7..c0655a7c29e113297484a53d72cc5ea0affbe8ce 100644
|
|
--- a/src/main/java/org/bukkit/Registry.java
|
|
+++ b/src/main/java/org/bukkit/Registry.java
|
|
@@ -211,6 +211,15 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
|
|
return GameEvent.getByKey(key);
|
|
}
|
|
};
|
|
+ // Paper start
|
|
+ /**
|
|
+ * Configured structures.
|
|
+ * @see io.papermc.paper.world.structure.ConfiguredStructure
|
|
+ * @deprecated use {@link #STRUCTURE}
|
|
+ */
|
|
+ @Deprecated(forRemoval = true)
|
|
+ Registry<io.papermc.paper.world.structure.ConfiguredStructure> CONFIGURED_STRUCTURE = Bukkit.getRegistry(io.papermc.paper.world.structure.ConfiguredStructure.class);
|
|
+ // Paper end
|
|
|
|
/**
|
|
* Get the object by its key.
|