diff --git a/patches/api/Add-PaperRegistry.patch b/patches/api/Add-PaperRegistry.patch new file mode 100644 index 0000000000..ece8c14047 --- /dev/null +++ b/patches/api/Add-PaperRegistry.patch @@ -0,0 +1,112 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Wed, 2 Mar 2022 13:36:21 -0800 +Subject: [PATCH] Add PaperRegistry + + +diff --git a/src/main/java/io/papermc/paper/registry/Reference.java b/src/main/java/io/papermc/paper/registry/Reference.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/Reference.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.registry; ++ ++import org.bukkit.Keyed; ++import org.bukkit.NamespacedKey; ++import org.bukkit.Registry; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++/** ++ * Represents a reference to a server-backed registry value that may ++ * change. ++ * ++ * @param type of the value ++ */ ++public interface Reference extends Keyed { ++ ++ /** ++ * Gets the value from the registry with the key. ++ * ++ * @return the value ++ * @throws java.util.NoSuchElementException if there is no value with this key ++ */ ++ @NotNull T value(); ++ ++ /** ++ * Gets the value from the registry with the key. ++ * ++ * @return the value or null if it doesn't exist ++ */ ++ @Nullable T valueOrNull(); ++ ++ /** ++ * Creates a reference to a registered value. ++ * ++ * @param registry the registry the value is located in ++ * @param key the key to the value ++ * @param the type of the value ++ * @return a reference ++ */ ++ static @NotNull Reference create(@NotNull Registry registry, @NotNull NamespacedKey key) { ++ return new ReferenceImpl<>(registry, key); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/ReferenceImpl.java b/src/main/java/io/papermc/paper/registry/ReferenceImpl.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/ReferenceImpl.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.registry; ++ ++import org.bukkit.Keyed; ++import org.bukkit.NamespacedKey; ++import org.bukkit.Registry; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++import java.util.NoSuchElementException; ++ ++record ReferenceImpl(@NotNull Registry registry, @NotNull NamespacedKey key) implements Reference { ++ ++ @Override ++ public @NotNull T value() { ++ final T value = this.registry.get(this.key); ++ if (value == null) { ++ throw new NoSuchElementException("No such value with key " + this.key); ++ } ++ return value; ++ } ++ ++ @Override ++ public @Nullable T valueOrNull() { ++ return this.registry.get(this.key); ++ } ++ ++ @Override ++ public @NotNull NamespacedKey getKey() { ++ return this.key; ++ } ++} +diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/UnsafeValues.java ++++ b/src/main/java/org/bukkit/UnsafeValues.java +@@ -0,0 +0,0 @@ public interface UnsafeValues { + * Use this when sending custom packets, so that there are no collisions on the client or server. + */ + public int nextEntityId(); ++ ++ /** ++ * Gets the server-backed registry for a type. ++ * ++ * @param classOfT type ++ * @param type ++ * @return the server-backed registry ++ * @throws IllegalArgumentException if there isn't a registry for that type ++ */ ++ @org.jetbrains.annotations.NotNull Registry registryFor(Class classOfT); + // Paper end + } diff --git a/patches/api/Add-StructureLocateEvent.patch b/patches/api/Add-StructureLocateEvent.patch deleted file mode 100644 index 786e859e43..0000000000 --- a/patches/api/Add-StructureLocateEvent.patch +++ /dev/null @@ -1,167 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: dfsek -Date: Tue, 15 Sep 2020 21:59:16 -0700 -Subject: [PATCH] Add StructureLocateEvent - - -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..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/io/papermc/paper/event/world/StructureLocateEvent.java -@@ -0,0 +0,0 @@ -+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 before a structure/feature is located. -+ * This happens when: -+ *
    -+ *
  • The /locate command is used.
  • -+ *
  • An Eye of Ender is used.
  • -+ *
  • An Explorer/Treasure Map is activated.
  • -+ *
  • {@link World#locateNearestStructure(Location, StructureType, int, boolean)} is invoked.
  • -+ *
-+ */ -+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. -+ *

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

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

-+ * 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 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 cancelled; -+ } -+ -+ @Override -+ public void setCancelled(boolean cancel) { -+ this.cancelled = cancel; -+ } -+} diff --git a/patches/api/Add-StructuresLocateEvent.patch b/patches/api/Add-StructuresLocateEvent.patch new file mode 100644 index 0000000000..fbe9c1c60e --- /dev/null +++ b/patches/api/Add-StructuresLocateEvent.patch @@ -0,0 +1,461 @@ +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..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/event/world/StructureLocateEvent.java +@@ -0,0 +0,0 @@ ++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 before a structure/feature is located. ++ * This happens when: ++ *

    ++ *
  • The /locate command is used.
  • ++ *
  • An Eye of Ender is used.
  • ++ *
  • An Explorer/Treasure Map is activated.
  • ++ *
  • {@link World#locateNearestStructure(Location, StructureType, int, boolean)} is invoked.
  • ++ *
++ * @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. ++ *

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

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

++ * 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 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 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..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/event/world/StructuresLocateEvent.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.event.world; ++ ++import io.papermc.paper.world.structure.ConfiguredStructure; ++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.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++import java.util.ArrayList; ++import java.util.List; ++ ++/** ++ * Called before a set of configured structures is located. ++ * This happens when: ++ *

    ++ *
  • The /locate command is used.
  • ++ *
  • An Eye of Ender is used.
  • ++ *
  • An Explorer/Treasure Map is activated.
  • ++ *
  • A dolphin swims to a treasure location.
  • ++ *
  • A trade is done with a villager for a map.
  • ++ *
  • {@link World#locateNearestStructure(Location, org.bukkit.StructureType, int, boolean)} is invoked.
  • ++ *
++ */ ++public class StructuresLocateEvent extends WorldEvent implements Cancellable { ++ ++ private static final HandlerList HANDLER_LIST = new HandlerList(); ++ ++ private final Location origin; ++ private Result result; ++ private List configuredStructures; ++ private int radius; ++ private boolean findUnexplored; ++ private boolean cancelled; ++ ++ public StructuresLocateEvent(@NotNull World world, @NotNull Location origin, @NotNull List configuredStructures, int radius, boolean findUnexplored) { ++ super(world); ++ this.origin = origin; ++ this.configuredStructures = configuredStructures; ++ 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 ConfiguredStructure} 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. null if it has not. ++ * @see World#locateNearestStructure(Location, org.bukkit.StructureType, int, boolean) ++ */ ++ public @Nullable Result getResult() { ++ return this.result; ++ } ++ ++ /** ++ * Sets the result {@link Location} and {@link ConfiguredStructure}. 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 ConfiguredStructure} 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 ++ */ ++ public @NotNull List getConfiguredStructures() { ++ return this.configuredStructures; ++ } ++ ++ /** ++ * Sets the list of ConfiguredStructures that are valid targets for the search. ++ * ++ * @param configuredStructures a list of ConfiguredStructure targets ++ */ ++ public void setConfiguredStructures(@NotNull List configuredStructures) { ++ this.configuredStructures = new ArrayList<>(configuredStructures); ++ } ++ ++ /** ++ * 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; ++ } ++ ++ @Override ++ public @NotNull HandlerList getHandlers() { ++ return HANDLER_LIST; ++ } ++ ++ public static @NotNull HandlerList getHandlerList() { ++ return HANDLER_LIST; ++ } ++ ++ /** ++ * Result for {@link StructuresLocateEvent}. ++ */ ++ public record Result(@NotNull Location position, @NotNull ConfiguredStructure configuredStructure) { ++ } ++} +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..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/world/structure/ConfiguredStructure.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.world.structure; ++ ++import io.papermc.paper.registry.Reference; ++import org.bukkit.Keyed; ++import org.bukkit.NamespacedKey; ++import org.bukkit.Registry; ++import org.bukkit.StructureType; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.Objects; ++ ++/** ++ * Represents a configured structure each with a ++ * {@link StructureType}. Multiple ConfiguredStructures can have ++ * the same {@link StructureType}. ++ */ ++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"); ++ ++ 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)); ++ } ++} +diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/Registry.java ++++ b/src/main/java/org/bukkit/Registry.java +@@ -0,0 +0,0 @@ public interface Registry extends Iterable { + return GameEvent.getByKey(key); + } + }; ++ // Paper start ++ /** ++ * Configured structures. ++ * @see io.papermc.paper.world.structure.ConfiguredStructure ++ */ ++ Registry CONFIGURED_STRUCTURE = Bukkit.getUnsafe().registryFor(io.papermc.paper.world.structure.ConfiguredStructure.class); ++ // Paper end + + /** + * Get the object by its key. diff --git a/patches/api/Item-Rarity-API.patch b/patches/api/Item-Rarity-API.patch index bb76044813..fa67b39d2b 100644 --- a/patches/api/Item-Rarity-API.patch +++ b/patches/api/Item-Rarity-API.patch @@ -65,9 +65,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/org/bukkit/UnsafeValues.java +++ b/src/main/java/org/bukkit/UnsafeValues.java @@ -0,0 +0,0 @@ public interface UnsafeValues { - * Use this when sending custom packets, so that there are no collisions on the client or server. + * @throws IllegalArgumentException if there isn't a registry for that type */ - public int nextEntityId(); + @org.jetbrains.annotations.NotNull Registry registryFor(Class classOfT); + + /** + * Gets the item rarity of a material. The material MUST be an item. diff --git a/patches/api/More-PotionEffectType-API.patch b/patches/api/More-PotionEffectType-API.patch index 0c745c1469..661dd040e4 100644 --- a/patches/api/More-PotionEffectType-API.patch +++ b/patches/api/More-PotionEffectType-API.patch @@ -9,10 +9,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/org/bukkit/Registry.java +++ b/src/main/java/org/bukkit/Registry.java @@ -0,0 +0,0 @@ public interface Registry extends Iterable { - return GameEvent.getByKey(key); - } - }; -+ // Paper start + * @see io.papermc.paper.world.structure.ConfiguredStructure + */ + Registry CONFIGURED_STRUCTURE = Bukkit.getUnsafe().registryFor(io.papermc.paper.world.structure.ConfiguredStructure.class); + /** + * Potion effect types. + * @@ -32,10 +31,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return Arrays.stream(org.bukkit.potion.PotionEffectType.values()).iterator(); + } + }; -+ // Paper end + // Paper end /** - * Get the object by its key. diff --git a/src/main/java/org/bukkit/potion/PotionEffectType.java b/src/main/java/org/bukkit/potion/PotionEffectType.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/potion/PotionEffectType.java diff --git a/patches/server/Add-PaperRegistry.patch b/patches/server/Add-PaperRegistry.patch new file mode 100644 index 0000000000..553f47c4a5 --- /dev/null +++ b/patches/server/Add-PaperRegistry.patch @@ -0,0 +1,220 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Wed, 2 Mar 2022 13:33:08 -0800 +Subject: [PATCH] Add PaperRegistry + +PaperRegistry is a server-backed impl of bukkit's Registry interface + +diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistry.java b/src/main/java/io/papermc/paper/registry/PaperRegistry.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/PaperRegistry.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.registry; ++ ++import com.google.common.base.Preconditions; ++import com.google.common.base.Suppliers; ++import net.minecraft.core.Holder; ++import net.minecraft.core.Registry; ++import net.minecraft.core.RegistryAccess; ++import net.minecraft.resources.ResourceKey; ++import net.minecraft.resources.ResourceLocation; ++import net.minecraft.server.MinecraftServer; ++import org.bukkit.Keyed; ++import org.bukkit.NamespacedKey; ++import org.bukkit.craftbukkit.util.CraftNamespacedKey; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++import java.util.Collections; ++import java.util.HashMap; ++import java.util.Iterator; ++import java.util.Map; ++import java.util.Objects; ++import java.util.Optional; ++import java.util.function.Supplier; ++ ++@DefaultQualifier(NonNull.class) ++public abstract class PaperRegistry implements org.bukkit.Registry { ++ ++ @SuppressWarnings("FieldMayBeFinal") // non-final for testing ++ private static Supplier REGISTRY_ACCESS = Suppliers.memoize(() -> MinecraftServer.getServer().registryAccess()); ++ private static final Map, PaperRegistry> INTERNAL_REGISTRIES = new HashMap<>(); ++ public static final Map, PaperRegistry> REGISTRIES = Collections.unmodifiableMap(INTERNAL_REGISTRIES); ++ private static final Map, PaperRegistry> REGISTRY_BY_API_CLASS = new HashMap<>(); ++ private static final Map>, PaperRegistry> REGISTRY_BY_RES_KEY = new HashMap<>(); ++ ++ private boolean registered; ++ private final RegistryKey registryKey; ++ private final Supplier> registry; ++ private final Map cache = new HashMap<>(); ++ ++ public PaperRegistry(RegistryKey registryKey) { ++ this.registryKey = registryKey; ++ this.registry = Suppliers.memoize(() -> REGISTRY_ACCESS.get().registryOrThrow(this.registryKey.resourceKey())); ++ } ++ ++ @Override ++ public @Nullable API get(NamespacedKey key) { ++ return this.cache.computeIfAbsent(key, k -> { ++ final @Nullable MINECRAFT nms = this.registry.get().get(CraftNamespacedKey.toMinecraft(k)); ++ if (nms != null) { ++ return this.convertToApi(k, nms); ++ } ++ return null; ++ }); ++ } ++ ++ public abstract API convertToApi(NamespacedKey key, MINECRAFT nms); ++ ++ public API convertToApi(ResourceLocation resourceLocation, MINECRAFT nms) { ++ return this.convertToApi(CraftNamespacedKey.fromMinecraft(resourceLocation), nms); ++ } ++ ++ public API convertToApi(Holder nmsHolder) { ++ final Optional> key = nmsHolder.unwrapKey(); ++ if (nmsHolder.isBound() && key.isPresent()) { ++ return this.convertToApi(key.get().location(), nmsHolder.value()); ++ } else if (!nmsHolder.isBound() && key.isPresent()) { ++ return this.convertToApi(key.get().location(), this.registry.get().getOrThrow(key.get())); ++ } else if (nmsHolder.isBound() && key.isEmpty()) { ++ final @Nullable ResourceLocation loc = this.registry.get().getKey(nmsHolder.value()); ++ if (loc != null) { ++ return this.convertToApi(loc, nmsHolder.value()); ++ } ++ } ++ throw new IllegalStateException("Cannot convert " + nmsHolder + " to an API type in: " + this.registryKey); ++ } ++ ++ public MINECRAFT getMinecraftValue(API apiValue) { ++ return this.registry.get().getOptional(CraftNamespacedKey.toMinecraft(apiValue.getKey())).orElseThrow(); ++ } ++ ++ public Holder getMinecraftHolder(API apiValue) { ++ return this.registry.get().getHolderOrThrow(ResourceKey.create(this.registryKey.resourceKey(), CraftNamespacedKey.toMinecraft(apiValue.getKey()))); ++ } ++ ++ @Override ++ public Iterator iterator() { ++ return this.registry.get().keySet().stream().map(key -> this.get(CraftNamespacedKey.fromMinecraft(key))).iterator(); ++ } ++ ++ public void clearCache() { ++ this.cache.clear(); ++ } ++ ++ public void register() { ++ if (this.registered) { ++ throw new IllegalStateException("Already registered: " + this.registryKey.apiClass()); ++ } ++ INTERNAL_REGISTRIES.put(this.registryKey, this); ++ REGISTRY_BY_API_CLASS.put(this.registryKey.apiClass(), this); ++ REGISTRY_BY_RES_KEY.put(this.registryKey.resourceKey(), this); ++ this.registered = true; ++ } ++ ++ @Override ++ public boolean equals(@Nullable Object o) { ++ if (this == o) return true; ++ if (o == null || !PaperRegistry.class.isAssignableFrom(o.getClass())) return false; ++ PaperRegistry that = (PaperRegistry) o; ++ return this.registryKey.equals(that.registryKey); ++ } ++ ++ @Override ++ public int hashCode() { ++ return Objects.hash(this.registryKey); ++ } ++ ++ protected static Supplier> registryFor(ResourceKey> registryKey) { ++ return Suppliers.memoize(() -> REGISTRY_ACCESS.get().registryOrThrow(registryKey)); ++ } ++ ++ public static void clearCaches() { ++ for (PaperRegistry registry : INTERNAL_REGISTRIES.values()) { ++ registry.clearCache(); ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ public static PaperRegistry getRegistry(Class classOfT) { ++ Preconditions.checkArgument(REGISTRY_BY_API_CLASS.containsKey(classOfT), "No registry for that type"); ++ return (PaperRegistry) REGISTRY_BY_API_CLASS.get(classOfT); ++ } ++ ++ @SuppressWarnings("unchecked") ++ public static PaperRegistry getRegistry(ResourceKey> resourceKey) { ++ Preconditions.checkArgument(REGISTRY_BY_RES_KEY.containsKey(resourceKey)); ++ return (PaperRegistry) REGISTRY_BY_RES_KEY.get(resourceKey); ++ } ++ ++ @SuppressWarnings("unchecked") ++ public static PaperRegistry getRegistry(RegistryKey registryKey) { ++ Preconditions.checkArgument(INTERNAL_REGISTRIES.containsKey(registryKey)); ++ return (PaperRegistry) INTERNAL_REGISTRIES.get(registryKey); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/RegistryKey.java b/src/main/java/io/papermc/paper/registry/RegistryKey.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/RegistryKey.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.registry; ++ ++import net.minecraft.core.Registry; ++import net.minecraft.resources.ResourceKey; ++import org.bukkit.Keyed; ++ ++public record RegistryKey(Class apiClass, ResourceKey> resourceKey) { ++} +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop Registry registryFor(Class classOfT) { ++ return io.papermc.paper.registry.PaperRegistry.getRegistry(classOfT); ++ } + // Paper end + + /** +diff --git a/src/test/java/org/bukkit/support/AbstractTestingBase.java b/src/test/java/org/bukkit/support/AbstractTestingBase.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/test/java/org/bukkit/support/AbstractTestingBase.java ++++ b/src/test/java/org/bukkit/support/AbstractTestingBase.java +@@ -0,0 +0,0 @@ public abstract class AbstractTestingBase { + MultiPackResourceManager resourceManager = new MultiPackResourceManager(PackType.SERVER_DATA, Collections.singletonList(new VanillaPackResources(ServerPacksSource.BUILT_IN_METADATA, "minecraft"))); + // add tags and loot tables for unit tests + RegistryAccess.Frozen registry = RegistryAccess.builtinCopy().freeze(); ++ // Paper start ++ try { ++ java.lang.reflect.Field field = io.papermc.paper.registry.PaperRegistry.class.getDeclaredField("REGISTRY_ACCESS"); ++ field.trySetAccessible(); ++ field.set(null, com.google.common.base.Suppliers.ofInstance(registry)); ++ } catch (ReflectiveOperationException ex) { ++ throw new IllegalStateException("Could not reflectively set RegistryAccess in PaperRegistry", ex); ++ } ++ // Paper end + // Register vanilla pack + DATA_PACK = ReloadableServerResources.loadResources(resourceManager, registry, Commands.CommandSelection.DEDICATED, 0, MoreExecutors.directExecutor(), MoreExecutors.directExecutor()).join(); + // Bind tags diff --git a/patches/server/Add-StructureLocateEvent.patch b/patches/server/Add-StructureLocateEvent.patch deleted file mode 100644 index c24e18f5ee..0000000000 --- a/patches/server/Add-StructureLocateEvent.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: dfsek -Date: Wed, 16 Sep 2020 01:12:29 -0700 -Subject: [PATCH] Add StructureLocateEvent - - -diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -@@ -0,0 +0,0 @@ public abstract class ChunkGenerator implements BiomeManager.NoiseBiomeSource { - - @Nullable - public Pair>> findNearestMapFeature(ServerLevel worldserver, HolderSet> holderset, BlockPos center, int radius, boolean skipExistingChunks) { -+ // Paper start -+ /*org.bukkit.World world1 = worldserver.getWorld(); -+ org.bukkit.Location originLocation = new org.bukkit.Location(world1, center.getX(), center.getY(), center.getZ()); -+ io.papermc.paper.event.world.StructureLocateEvent event = new io.papermc.paper.event.world.StructureLocateEvent(world1, originLocation, org.bukkit.StructureType.getStructureTypes().get(structureFeature.getFeatureName()), radius, skipExistingChunks); -+ if(!event.callEvent()) return null; -+ // If event call set a final location, skip structure finding and just return set result. -+ if(event.getResult() != null) return new BlockPos(event.getResult().getBlockX(), event.getResult().getBlockY(), event.getResult().getBlockZ()); -+ // Get origin location (re)defined by event call. -+ center = new BlockPos(event.getOrigin().getBlockX(), event.getOrigin().getBlockY(), event.getOrigin().getBlockZ()); -+ // Get radius and whether to find unexplored structures (re)defined by event call. -+ radius = event.getRadius(); -+ skipExistingChunks = event.shouldFindUnexplored(); -+ structureFeature = StructureFeature.STRUCTURES_REGISTRY.get(event.getType().getName());*/ -+ // Paper end - Set> set = (Set) holderset.stream().flatMap((holder) -> { - return ((ConfiguredStructureFeature) holder.value()).biomes().stream(); - }).collect(Collectors.toSet()); diff --git a/patches/server/Add-StructuresLocateEvent.patch b/patches/server/Add-StructuresLocateEvent.patch new file mode 100644 index 0000000000..e44e751051 --- /dev/null +++ b/patches/server/Add-StructuresLocateEvent.patch @@ -0,0 +1,224 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: dfsek +Date: Wed, 16 Sep 2020 01:12:29 -0700 +Subject: [PATCH] Add StructuresLocateEvent + +Co-authored-by: Jake Potrebic + +diff --git a/src/main/java/io/papermc/paper/registry/RegistryKey.java b/src/main/java/io/papermc/paper/registry/RegistryKey.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/io/papermc/paper/registry/RegistryKey.java ++++ b/src/main/java/io/papermc/paper/registry/RegistryKey.java +@@ -0,0 +0,0 @@ + package io.papermc.paper.registry; + ++import io.papermc.paper.world.structure.ConfiguredStructure; + import net.minecraft.core.Registry; + import net.minecraft.resources.ResourceKey; ++import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature; + import org.bukkit.Keyed; + + public record RegistryKey(Class apiClass, ResourceKey> resourceKey) { ++ ++ public static final RegistryKey> CONFIGURED_STRUCTURE_REGISTRY = new RegistryKey<>(ConfiguredStructure.class, Registry.CONFIGURED_STRUCTURE_FEATURE_REGISTRY); ++ + } +diff --git a/src/main/java/io/papermc/paper/world/structure/PaperConfiguredStructure.java b/src/main/java/io/papermc/paper/world/structure/PaperConfiguredStructure.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/world/structure/PaperConfiguredStructure.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.world.structure; ++ ++import io.papermc.paper.registry.PaperRegistry; ++import io.papermc.paper.registry.RegistryKey; ++import net.minecraft.core.Registry; ++import net.minecraft.resources.ResourceLocation; ++import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature; ++import net.minecraft.world.level.levelgen.feature.StructureFeature; ++import org.bukkit.NamespacedKey; ++import org.bukkit.StructureType; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++import java.util.Objects; ++import java.util.function.Supplier; ++ ++@DefaultQualifier(NonNull.class) ++public final class PaperConfiguredStructure { ++ ++ private PaperConfiguredStructure() { ++ } ++ ++ public static void init() { ++ new ConfiguredStructureRegistry().register(); ++ } ++ ++ static final class ConfiguredStructureRegistry extends PaperRegistry> { ++ ++ private static final Supplier>> STRUCTURE_FEATURE_REGISTRY = registryFor(Registry.STRUCTURE_FEATURE_REGISTRY); ++ ++ public ConfiguredStructureRegistry() { ++ super(RegistryKey.CONFIGURED_STRUCTURE_REGISTRY); ++ } ++ ++ @Override ++ public ConfiguredStructure convertToApi(NamespacedKey key, ConfiguredStructureFeature nms) { ++ final ResourceLocation structureFeatureLoc = Objects.requireNonNull(STRUCTURE_FEATURE_REGISTRY.get().getKey(nms.feature)); ++ final StructureType structureType = Objects.requireNonNull(StructureType.getStructureTypes().get(structureFeatureLoc.getPath()), structureFeatureLoc + " could not be converted to an API type"); ++ return new ConfiguredStructure(key, structureType); ++ } ++ } ++} +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop>> findNearestMapFeature(ServerLevel worldserver, HolderSet> holderset, BlockPos center, int radius, boolean skipExistingChunks) { ++ // Paper start - StructureLocateEvent ++ final org.bukkit.World world = worldserver.getWorld(); ++ final org.bukkit.Location origin = net.minecraft.server.MCUtil.toLocation(worldserver, center); ++ final var paperRegistry = io.papermc.paper.registry.PaperRegistry.getRegistry(io.papermc.paper.registry.RegistryKey.CONFIGURED_STRUCTURE_REGISTRY); ++ final List configuredStructures = new ArrayList<>(); ++ for (Holder> holder : holderset) { ++ configuredStructures.add(paperRegistry.convertToApi(holder)); ++ } ++ final io.papermc.paper.event.world.StructuresLocateEvent event = new io.papermc.paper.event.world.StructuresLocateEvent(world, origin, configuredStructures, radius, skipExistingChunks); ++ if (!event.callEvent()) { ++ return null; ++ } ++ if (event.getResult() != null) { ++ return Pair.of(net.minecraft.server.MCUtil.toBlockPosition(event.getResult().position()), paperRegistry.getMinecraftHolder(event.getResult().configuredStructure())); ++ } ++ center = net.minecraft.server.MCUtil.toBlockPosition(event.getOrigin()); ++ radius = event.getRadius(); ++ skipExistingChunks = event.shouldFindUnexplored(); ++ holderset = HolderSet.direct(paperRegistry::getMinecraftHolder, event.getConfiguredStructures()); ++ // Paper end + Set> set = (Set) holderset.stream().flatMap((holder) -> { + return ((ConfiguredStructureFeature) holder.value()).biomes().stream(); + }).collect(Collectors.toSet()); +diff --git a/src/main/java/net/minecraft/world/level/levelgen/feature/ConfiguredStructureFeature.java b/src/main/java/net/minecraft/world/level/levelgen/feature/ConfiguredStructureFeature.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/feature/ConfiguredStructureFeature.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/feature/ConfiguredStructureFeature.java +@@ -0,0 +0,0 @@ public class ConfiguredStructureFeature biomes; + public final Map spawnOverrides; + public final boolean adaptNoise; ++ static { io.papermc.paper.world.structure.PaperConfiguredStructure.init(); } // Paper + + public ConfiguredStructureFeature(F feature, FC config, HolderSet biomes, boolean bl, Map map) { + this.feature = feature; +diff --git a/src/test/java/io/papermc/paper/world/structure/ConfiguredStructureTest.java b/src/test/java/io/papermc/paper/world/structure/ConfiguredStructureTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/test/java/io/papermc/paper/world/structure/ConfiguredStructureTest.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.world.structure; ++ ++import io.papermc.paper.registry.Reference; ++import net.minecraft.data.BuiltinRegistries; ++import net.minecraft.resources.ResourceKey; ++import net.minecraft.resources.ResourceLocation; ++import net.minecraft.server.Bootstrap; ++import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature; ++import net.minecraft.world.level.levelgen.structure.BuiltinStructures; ++import org.bukkit.NamespacedKey; ++import org.bukkit.craftbukkit.util.CraftNamespacedKey; ++import org.bukkit.support.AbstractTestingBase; ++import org.junit.AfterClass; ++import org.junit.BeforeClass; ++import org.junit.Test; ++ ++import java.io.PrintStream; ++import java.lang.reflect.Field; ++import java.lang.reflect.Modifier; ++import java.util.LinkedHashMap; ++import java.util.Map; ++import java.util.StringJoiner; ++ ++import static org.junit.Assert.assertEquals; ++import static org.junit.Assert.assertNotNull; ++import static org.junit.Assert.assertTrue; ++ ++public class ConfiguredStructureTest extends AbstractTestingBase { ++ ++ private static final Map BUILT_IN_STRUCTURES = new LinkedHashMap<>(); ++ private static final Map> DEFAULT_CONFIGURED_STRUCTURES = new LinkedHashMap<>(); ++ ++ private static PrintStream out; ++ ++ @BeforeClass ++ public static void collectStructures() throws ReflectiveOperationException { ++ out = System.out; ++ System.setOut(Bootstrap.STDOUT); ++ for (Field field : BuiltinStructures.class.getDeclaredFields()) { ++ if (field.getType().equals(ResourceKey.class) && Modifier.isStatic(field.getModifiers())) { ++ BUILT_IN_STRUCTURES.put(((ResourceKey) field.get(null)).location(), field.getName()); ++ } ++ } ++ for (Field field : ConfiguredStructure.class.getDeclaredFields()) { ++ if (field.getType().equals(Reference.class) && Modifier.isStatic(field.getModifiers())) { ++ final Reference ref = (Reference) field.get(null); ++ DEFAULT_CONFIGURED_STRUCTURES.put(ref.getKey(), ref); ++ } ++ } ++ } ++ ++ @Test ++ public void testMinecraftToApi() { ++ assertEquals("configured structure maps should be the same size", BUILT_IN_STRUCTURES.size(), BuiltinRegistries.CONFIGURED_STRUCTURE_FEATURE.size()); ++ ++ Map> missing = new LinkedHashMap<>(); ++ for (ConfiguredStructureFeature feature : BuiltinRegistries.CONFIGURED_STRUCTURE_FEATURE) { ++ final ResourceLocation key = BuiltinRegistries.CONFIGURED_STRUCTURE_FEATURE.getKey(feature); ++ assertNotNull("Missing built-in registry key", key); ++ if (DEFAULT_CONFIGURED_STRUCTURES.get(CraftNamespacedKey.fromMinecraft(key)) == null) { ++ missing.put(key, feature); ++ } ++ } ++ ++ assertTrue(printMissing(missing), missing.isEmpty()); ++ } ++ ++ @Test ++ public void testApiToMinecraft() { ++ for (NamespacedKey apiKey : DEFAULT_CONFIGURED_STRUCTURES.keySet()) { ++ assertTrue(apiKey + " does not have a minecraft counterpart", BuiltinRegistries.CONFIGURED_STRUCTURE_FEATURE.containsKey(CraftNamespacedKey.toMinecraft(apiKey))); ++ } ++ } ++ ++ private static String printMissing(Map> missing) { ++ final StringJoiner joiner = new StringJoiner("\n", "Missing: \n", ""); ++ ++ missing.forEach((key, configuredFeature) -> { ++ joiner.add("public static final Reference " + BUILT_IN_STRUCTURES.get(key) + " = create(\"" + key.getPath() + "\");"); ++ }); ++ ++ return joiner.toString(); ++ } ++ ++ @AfterClass ++ public static void after() { ++ System.setOut(out); ++ } ++} diff --git a/patches/server/Custom-Potion-Mixes.patch b/patches/server/Custom-Potion-Mixes.patch index 291bcf5c8d..62fa94b4da 100644 --- a/patches/server/Custom-Potion-Mixes.patch +++ b/patches/server/Custom-Potion-Mixes.patch @@ -28,9 +28,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop Registry registryFor(Class classOfT) { + return io.papermc.paper.registry.PaperRegistry.getRegistry(classOfT); } + + @Override